aboutsummaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
authorPeter Zijlstra <peterz@infradead.org>2016-01-08 04:01:18 -0500
committerIngo Molnar <mingo@kernel.org>2016-01-21 12:54:20 -0500
commit3e349507d12de93b08b0aa814fc2aa0dee91c5ba (patch)
tree8facf49d1fe8af945ff671f49fb8e43abf99e9ae /kernel
parent5947f6576e2edee1189b00bf5b2a3f2c653caa6b (diff)
perf: Fix perf_enable_on_exec() event scheduling
There are two problems with the current perf_enable_on_exec() event scheduling: - the newly enabled events will be immediately scheduled irrespective of their ctx event list order. - there's a hole in the ctx->lock between scheduling the events out and putting them back on. Esp. the latter issue is a real problem because a hole in event scheduling leaves the thing in an observable inconsistent state, confusing things. Fix both issues by first doing the enable iteration and at the end, when there are newly enabled events, reschedule the ctx in one go. Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> Cc: Arnaldo Carvalho de Melo <acme@redhat.com> Cc: David Ahern <dsahern@gmail.com> Cc: Jiri Olsa <jolsa@redhat.com> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Namhyung Kim <namhyung@kernel.org> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Stephane Eranian <eranian@google.com> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Vince Weaver <vincent.weaver@maine.edu> Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'kernel')
-rw-r--r--kernel/events/core.c47
1 files changed, 27 insertions, 20 deletions
diff --git a/kernel/events/core.c b/kernel/events/core.c
index 751538ce19a7..0679e73f5f63 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -2036,7 +2036,8 @@ static void add_event_to_ctx(struct perf_event *event,
2036 event->tstamp_stopped = tstamp; 2036 event->tstamp_stopped = tstamp;
2037} 2037}
2038 2038
2039static void task_ctx_sched_out(struct perf_event_context *ctx); 2039static void task_ctx_sched_out(struct perf_cpu_context *cpuctx,
2040 struct perf_event_context *ctx);
2040static void 2041static void
2041ctx_sched_in(struct perf_event_context *ctx, 2042ctx_sched_in(struct perf_event_context *ctx,
2042 struct perf_cpu_context *cpuctx, 2043 struct perf_cpu_context *cpuctx,
@@ -2067,6 +2068,17 @@ static void ___perf_install_in_context(void *info)
2067 add_event_to_ctx(event, ctx); 2068 add_event_to_ctx(event, ctx);
2068} 2069}
2069 2070
2071static void ctx_resched(struct perf_cpu_context *cpuctx,
2072 struct perf_event_context *task_ctx)
2073{
2074 perf_pmu_disable(cpuctx->ctx.pmu);
2075 if (task_ctx)
2076 task_ctx_sched_out(cpuctx, task_ctx);
2077 cpu_ctx_sched_out(cpuctx, EVENT_ALL);
2078 perf_event_sched_in(cpuctx, task_ctx, current);
2079 perf_pmu_enable(cpuctx->ctx.pmu);
2080}
2081
2070/* 2082/*
2071 * Cross CPU call to install and enable a performance event 2083 * Cross CPU call to install and enable a performance event
2072 * 2084 *
@@ -2087,7 +2099,7 @@ static int __perf_install_in_context(void *info)
2087 * If there was an active task_ctx schedule it out. 2099 * If there was an active task_ctx schedule it out.
2088 */ 2100 */
2089 if (task_ctx) 2101 if (task_ctx)
2090 task_ctx_sched_out(task_ctx); 2102 task_ctx_sched_out(cpuctx, task_ctx);
2091 2103
2092 /* 2104 /*
2093 * If the context we're installing events in is not the 2105 * If the context we're installing events in is not the
@@ -2629,10 +2641,9 @@ void __perf_event_task_sched_out(struct task_struct *task,
2629 perf_cgroup_sched_out(task, next); 2641 perf_cgroup_sched_out(task, next);
2630} 2642}
2631 2643
2632static void task_ctx_sched_out(struct perf_event_context *ctx) 2644static void task_ctx_sched_out(struct perf_cpu_context *cpuctx,
2645 struct perf_event_context *ctx)
2633{ 2646{
2634 struct perf_cpu_context *cpuctx = __get_cpu_context(ctx);
2635
2636 if (!cpuctx->task_ctx) 2647 if (!cpuctx->task_ctx)
2637 return; 2648 return;
2638 2649
@@ -3096,34 +3107,30 @@ static int event_enable_on_exec(struct perf_event *event,
3096static void perf_event_enable_on_exec(int ctxn) 3107static void perf_event_enable_on_exec(int ctxn)
3097{ 3108{
3098 struct perf_event_context *ctx, *clone_ctx = NULL; 3109 struct perf_event_context *ctx, *clone_ctx = NULL;
3110 struct perf_cpu_context *cpuctx;
3099 struct perf_event *event; 3111 struct perf_event *event;
3100 unsigned long flags; 3112 unsigned long flags;
3101 int enabled = 0; 3113 int enabled = 0;
3102 int ret;
3103 3114
3104 local_irq_save(flags); 3115 local_irq_save(flags);
3105 ctx = current->perf_event_ctxp[ctxn]; 3116 ctx = current->perf_event_ctxp[ctxn];
3106 if (!ctx || !ctx->nr_events) 3117 if (!ctx || !ctx->nr_events)
3107 goto out; 3118 goto out;
3108 3119
3109 raw_spin_lock(&ctx->lock); 3120 cpuctx = __get_cpu_context(ctx);
3110 task_ctx_sched_out(ctx); 3121 perf_ctx_lock(cpuctx, ctx);
3111 3122 list_for_each_entry(event, &ctx->event_list, event_entry)
3112 list_for_each_entry(event, &ctx->event_list, event_entry) { 3123 enabled |= event_enable_on_exec(event, ctx);
3113 ret = event_enable_on_exec(event, ctx);
3114 if (ret)
3115 enabled = 1;
3116 }
3117 3124
3118 /* 3125 /*
3119 * Unclone this context if we enabled any event. 3126 * Unclone and reschedule this context if we enabled any event.
3120 */ 3127 */
3121 if (enabled) 3128 if (enabled) {
3122 clone_ctx = unclone_ctx(ctx); 3129 clone_ctx = unclone_ctx(ctx);
3130 ctx_resched(cpuctx, ctx);
3131 }
3132 perf_ctx_unlock(cpuctx, ctx);
3123 3133
3124 raw_spin_unlock(&ctx->lock);
3125
3126 perf_event_context_sched_in(ctx, ctx->task);
3127out: 3134out:
3128 local_irq_restore(flags); 3135 local_irq_restore(flags);
3129 3136
@@ -8737,7 +8744,7 @@ static void perf_event_exit_task_context(struct task_struct *child, int ctxn)
8737 * incremented the context's refcount before we do put_ctx below. 8744 * incremented the context's refcount before we do put_ctx below.
8738 */ 8745 */
8739 raw_spin_lock(&child_ctx->lock); 8746 raw_spin_lock(&child_ctx->lock);
8740 task_ctx_sched_out(child_ctx); 8747 task_ctx_sched_out(__get_cpu_context(child_ctx), child_ctx);
8741 child->perf_event_ctxp[ctxn] = NULL; 8748 child->perf_event_ctxp[ctxn] = NULL;
8742 8749
8743 /* 8750 /*