diff options
| author | Peter Zijlstra <a.p.zijlstra@chello.nl> | 2010-09-02 10:50:03 -0400 |
|---|---|---|
| committer | Ingo Molnar <mingo@elte.hu> | 2010-09-09 14:46:33 -0400 |
| commit | 8dc85d547285668e509f86c177bcd4ea055bcaaf (patch) | |
| tree | 5474dd6c31952b342f0a3f1bcec09e3049129264 | |
| parent | eb184479874238393ac186c4e054d24311c34aaa (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.h | 1 | ||||
| -rw-r--r-- | include/linux/sched.h | 8 | ||||
| -rw-r--r-- | kernel/perf_event.c | 336 |
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 | ||
| 1161 | struct rcu_node; | 1161 | struct rcu_node; |
| 1162 | 1162 | ||
| 1163 | enum perf_event_task_context { | ||
| 1164 | perf_invalid_context = -1, | ||
| 1165 | perf_hw_context = 0, | ||
| 1166 | perf_nr_task_contexts, | ||
| 1167 | }; | ||
| 1168 | |||
| 1163 | struct task_struct { | 1169 | struct 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 | */ |
| 150 | static struct perf_event_context * | 150 | static struct perf_event_context * |
| 151 | perf_lock_task_context(struct task_struct *task, unsigned long *flags) | 151 | perf_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(); |
| 156 | retry: | 156 | retry: |
| 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 | */ |
| 189 | static struct perf_event_context *perf_pin_task_context(struct task_struct *task) | 189 | static struct perf_event_context * |
| 190 | perf_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 | /* | 1183 | void 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 | */ | ||
| 1193 | void 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 | */ | ||
| 1253 | void 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 | |||
| 1251 | static void task_ctx_sched_out(struct perf_event_context *ctx, | 1264 | static 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); | ||
