aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPeter Zijlstra <a.p.zijlstra@chello.nl>2010-09-02 10:50:03 -0400
committerIngo Molnar <mingo@elte.hu>2010-09-09 14:46:33 -0400
commit8dc85d547285668e509f86c177bcd4ea055bcaaf (patch)
tree5474dd6c31952b342f0a3f1bcec09e3049129264
parenteb184479874238393ac186c4e054d24311c34aaa (diff)
perf: Multiple task contexts
Provide the infrastructure for multiple task contexts. A more flexible approach would have resulted in more pointer chases in the scheduling hot-paths. This approach has the limitation of a static number of task contexts. Since I expect most external PMUs to be system wide, or at least node wide (as per the intel uncore unit) they won't actually need a task context. Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl> Cc: paulus <paulus@samba.org> Cc: stephane eranian <eranian@googlemail.com> Cc: Robert Richter <robert.richter@amd.com> Cc: Frederic Weisbecker <fweisbec@gmail.com> Cc: Lin Ming <ming.m.lin@intel.com> Cc: Yanmin <yanmin_zhang@linux.intel.com> LKML-Reference: <new-submission> Signed-off-by: Ingo Molnar <mingo@elte.hu>
-rw-r--r--include/linux/perf_event.h1
-rw-r--r--include/linux/sched.h8
-rw-r--r--kernel/perf_event.c336
3 files changed, 239 insertions, 106 deletions
diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h
index 22155ef3b362..9ecfd856ce6e 100644
--- a/include/linux/perf_event.h
+++ b/include/linux/perf_event.h
@@ -572,6 +572,7 @@ struct pmu {
572 572
573 int * __percpu pmu_disable_count; 573 int * __percpu pmu_disable_count;
574 struct perf_cpu_context * __percpu pmu_cpu_context; 574 struct perf_cpu_context * __percpu pmu_cpu_context;
575 int task_ctx_nr;
575 576
576 /* 577 /*
577 * Fully disable/enable this PMU, can be used to protect from the PMI 578 * Fully disable/enable this PMU, can be used to protect from the PMI
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 1e2a6db2d7dd..89d6023c6f82 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -1160,6 +1160,12 @@ struct sched_rt_entity {
1160 1160
1161struct rcu_node; 1161struct rcu_node;
1162 1162
1163enum perf_event_task_context {
1164 perf_invalid_context = -1,
1165 perf_hw_context = 0,
1166 perf_nr_task_contexts,
1167};
1168
1163struct task_struct { 1169struct task_struct {
1164 volatile long state; /* -1 unrunnable, 0 runnable, >0 stopped */ 1170 volatile long state; /* -1 unrunnable, 0 runnable, >0 stopped */
1165 void *stack; 1171 void *stack;
@@ -1431,7 +1437,7 @@ struct task_struct {
1431 struct futex_pi_state *pi_state_cache; 1437 struct futex_pi_state *pi_state_cache;
1432#endif 1438#endif
1433#ifdef CONFIG_PERF_EVENTS 1439#ifdef CONFIG_PERF_EVENTS
1434 struct perf_event_context *perf_event_ctxp; 1440 struct perf_event_context *perf_event_ctxp[perf_nr_task_contexts];
1435 struct mutex perf_event_mutex; 1441 struct mutex perf_event_mutex;
1436 struct list_head perf_event_list; 1442 struct list_head perf_event_list;
1437#endif 1443#endif
diff --git a/kernel/perf_event.c b/kernel/perf_event.c
index 13d98d756347..7223ea875861 100644
--- a/kernel/perf_event.c
+++ b/kernel/perf_event.c
@@ -148,13 +148,13 @@ static u64 primary_event_id(struct perf_event *event)
148 * the context could get moved to another task. 148 * the context could get moved to another task.
149 */ 149 */
150static struct perf_event_context * 150static struct perf_event_context *
151perf_lock_task_context(struct task_struct *task, unsigned long *flags) 151perf_lock_task_context(struct task_struct *task, int ctxn, unsigned long *flags)
152{ 152{
153 struct perf_event_context *ctx; 153 struct perf_event_context *ctx;
154 154
155 rcu_read_lock(); 155 rcu_read_lock();
156retry: 156retry:
157 ctx = rcu_dereference(task->perf_event_ctxp); 157 ctx = rcu_dereference(task->perf_event_ctxp[ctxn]);
158 if (ctx) { 158 if (ctx) {
159 /* 159 /*
160 * If this context is a clone of another, it might 160 * If this context is a clone of another, it might
@@ -167,7 +167,7 @@ retry:
167 * can't get swapped on us any more. 167 * can't get swapped on us any more.
168 */ 168 */
169 raw_spin_lock_irqsave(&ctx->lock, *flags); 169 raw_spin_lock_irqsave(&ctx->lock, *flags);
170 if (ctx != rcu_dereference(task->perf_event_ctxp)) { 170 if (ctx != rcu_dereference(task->perf_event_ctxp[ctxn])) {
171 raw_spin_unlock_irqrestore(&ctx->lock, *flags); 171 raw_spin_unlock_irqrestore(&ctx->lock, *flags);
172 goto retry; 172 goto retry;
173 } 173 }
@@ -186,12 +186,13 @@ retry:
186 * can't get swapped to another task. This also increments its 186 * can't get swapped to another task. This also increments its
187 * reference count so that the context can't get freed. 187 * reference count so that the context can't get freed.
188 */ 188 */
189static struct perf_event_context *perf_pin_task_context(struct task_struct *task) 189static struct perf_event_context *
190perf_pin_task_context(struct task_struct *task, int ctxn)
190{ 191{
191 struct perf_event_context *ctx; 192 struct perf_event_context *ctx;
192 unsigned long flags; 193 unsigned long flags;
193 194
194 ctx = perf_lock_task_context(task, &flags); 195 ctx = perf_lock_task_context(task, ctxn, &flags);
195 if (ctx) { 196 if (ctx) {
196 ++ctx->pin_count; 197 ++ctx->pin_count;
197 raw_spin_unlock_irqrestore(&ctx->lock, flags); 198 raw_spin_unlock_irqrestore(&ctx->lock, flags);
@@ -1179,28 +1180,15 @@ static void perf_event_sync_stat(struct perf_event_context *ctx,
1179 } 1180 }
1180} 1181}
1181 1182
1182/* 1183void perf_event_context_sched_out(struct task_struct *task, int ctxn,
1183 * Called from scheduler to remove the events of the current task, 1184 struct task_struct *next)
1184 * with interrupts disabled.
1185 *
1186 * We stop each event and update the event value in event->count.
1187 *
1188 * This does not protect us against NMI, but disable()
1189 * sets the disabled bit in the control field of event _before_
1190 * accessing the event control register. If a NMI hits, then it will
1191 * not restart the event.
1192 */
1193void perf_event_task_sched_out(struct task_struct *task,
1194 struct task_struct *next)
1195{ 1185{
1196 struct perf_event_context *ctx = task->perf_event_ctxp; 1186 struct perf_event_context *ctx = task->perf_event_ctxp[ctxn];
1197 struct perf_event_context *next_ctx; 1187 struct perf_event_context *next_ctx;
1198 struct perf_event_context *parent; 1188 struct perf_event_context *parent;
1199 struct perf_cpu_context *cpuctx; 1189 struct perf_cpu_context *cpuctx;
1200 int do_switch = 1; 1190 int do_switch = 1;
1201 1191
1202 perf_sw_event(PERF_COUNT_SW_CONTEXT_SWITCHES, 1, 1, NULL, 0);
1203
1204 if (likely(!ctx)) 1192 if (likely(!ctx))
1205 return; 1193 return;
1206 1194
@@ -1210,7 +1198,7 @@ void perf_event_task_sched_out(struct task_struct *task,
1210 1198
1211 rcu_read_lock(); 1199 rcu_read_lock();
1212 parent = rcu_dereference(ctx->parent_ctx); 1200 parent = rcu_dereference(ctx->parent_ctx);
1213 next_ctx = next->perf_event_ctxp; 1201 next_ctx = next->perf_event_ctxp[ctxn];
1214 if (parent && next_ctx && 1202 if (parent && next_ctx &&
1215 rcu_dereference(next_ctx->parent_ctx) == parent) { 1203 rcu_dereference(next_ctx->parent_ctx) == parent) {
1216 /* 1204 /*
@@ -1229,8 +1217,8 @@ void perf_event_task_sched_out(struct task_struct *task,
1229 * XXX do we need a memory barrier of sorts 1217 * XXX do we need a memory barrier of sorts
1230 * wrt to rcu_dereference() of perf_event_ctxp 1218 * wrt to rcu_dereference() of perf_event_ctxp
1231 */ 1219 */
1232 task->perf_event_ctxp = next_ctx; 1220 task->perf_event_ctxp[ctxn] = next_ctx;
1233 next->perf_event_ctxp = ctx; 1221 next->perf_event_ctxp[ctxn] = ctx;
1234 ctx->task = next; 1222 ctx->task = next;
1235 next_ctx->task = task; 1223 next_ctx->task = task;
1236 do_switch = 0; 1224 do_switch = 0;
@@ -1248,6 +1236,31 @@ void perf_event_task_sched_out(struct task_struct *task,
1248 } 1236 }
1249} 1237}
1250 1238
1239#define for_each_task_context_nr(ctxn) \
1240 for ((ctxn) = 0; (ctxn) < perf_nr_task_contexts; (ctxn)++)
1241
1242/*
1243 * Called from scheduler to remove the events of the current task,
1244 * with interrupts disabled.
1245 *
1246 * We stop each event and update the event value in event->count.
1247 *
1248 * This does not protect us against NMI, but disable()
1249 * sets the disabled bit in the control field of event _before_
1250 * accessing the event control register. If a NMI hits, then it will
1251 * not restart the event.
1252 */
1253void perf_event_task_sched_out(struct task_struct *task,
1254 struct task_struct *next)
1255{
1256 int ctxn;
1257
1258 perf_sw_event(PERF_COUNT_SW_CONTEXT_SWITCHES, 1, 1, NULL, 0);
1259
1260 for_each_task_context_nr(ctxn)
1261 perf_event_context_sched_out(task, ctxn, next);
1262}
1263
1251static void task_ctx_sched_out(struct perf_event_context *ctx, 1264static void task_ctx_sched_out(struct perf_event_context *ctx,
1252 enum event_type_t event_type) 1265 enum event_type_t event_type)
1253{ 1266{
@@ -1366,38 +1379,23 @@ static void cpu_ctx_sched_in(struct perf_cpu_context *cpuctx,
1366 ctx_sched_in(ctx, cpuctx, event_type);