diff options
author | Stephane Eranian <eranian@google.com> | 2011-02-18 07:40:01 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2011-02-23 05:35:46 -0500 |
commit | 3f7cce3c18188a067d463749168bdda5abc5b0f7 (patch) | |
tree | 9c161a970a19176f26dc29811dc9ae4d64bfe8a8 /kernel/perf_event.c | |
parent | c97cf42219b7b6037d2f96c27a5f114f2383f828 (diff) |
perf_events: Fix rcu and locking issues with cgroup support
This patches ensures that we do not end up calling
perf_cgroup_from_task() when there is no cgroup event.
This avoids potential RCU and locking issues.
The change in perf_cgroup_set_timestamp() ensures we
check against ctx->nr_cgroups. It also avoids calling
perf_clock() tiwce in a row. It also ensures we do need
to grab ctx->lock before calling the function.
We drop update_cgrp_time() from task_clock_event_read()
because it is not needed. This also avoids having to
deal with perf_cgroup_from_task().
Thanks to Peter Zijlstra for his help on this.
Signed-off-by: Stephane Eranian <eranian@gmail.com>
Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl>
LKML-Reference: <4d5e76b8.815bdf0a.7ac3.774f@mx.google.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'kernel/perf_event.c')
-rw-r--r-- | kernel/perf_event.c | 40 |
1 files changed, 29 insertions, 11 deletions
diff --git a/kernel/perf_event.c b/kernel/perf_event.c index a0a6987fabc4..dadeaea4b3fc 100644 --- a/kernel/perf_event.c +++ b/kernel/perf_event.c | |||
@@ -201,6 +201,11 @@ __get_cpu_context(struct perf_event_context *ctx) | |||
201 | 201 | ||
202 | #ifdef CONFIG_CGROUP_PERF | 202 | #ifdef CONFIG_CGROUP_PERF |
203 | 203 | ||
204 | /* | ||
205 | * Must ensure cgroup is pinned (css_get) before calling | ||
206 | * this function. In other words, we cannot call this function | ||
207 | * if there is no cgroup event for the current CPU context. | ||
208 | */ | ||
204 | static inline struct perf_cgroup * | 209 | static inline struct perf_cgroup * |
205 | perf_cgroup_from_task(struct task_struct *task) | 210 | perf_cgroup_from_task(struct task_struct *task) |
206 | { | 211 | { |
@@ -268,28 +273,41 @@ static inline void update_cgrp_time_from_cpuctx(struct perf_cpu_context *cpuctx) | |||
268 | 273 | ||
269 | static inline void update_cgrp_time_from_event(struct perf_event *event) | 274 | static inline void update_cgrp_time_from_event(struct perf_event *event) |
270 | { | 275 | { |
271 | struct perf_cgroup *cgrp = perf_cgroup_from_task(current); | 276 | struct perf_cgroup *cgrp; |
277 | |||
272 | /* | 278 | /* |
273 | * do not update time when cgroup is not active | 279 | * ensure we access cgroup data only when needed and |
280 | * when we know the cgroup is pinned (css_get) | ||
274 | */ | 281 | */ |
275 | if (!event->cgrp || cgrp != event->cgrp) | 282 | if (!is_cgroup_event(event)) |
276 | return; | 283 | return; |
277 | 284 | ||
278 | __update_cgrp_time(event->cgrp); | 285 | cgrp = perf_cgroup_from_task(current); |
286 | /* | ||
287 | * Do not update time when cgroup is not active | ||
288 | */ | ||
289 | if (cgrp == event->cgrp) | ||
290 | __update_cgrp_time(event->cgrp); | ||
279 | } | 291 | } |
280 | 292 | ||
281 | static inline void | 293 | static inline void |
282 | perf_cgroup_set_timestamp(struct task_struct *task, u64 now) | 294 | perf_cgroup_set_timestamp(struct task_struct *task, |
295 | struct perf_event_context *ctx) | ||
283 | { | 296 | { |
284 | struct perf_cgroup *cgrp; | 297 | struct perf_cgroup *cgrp; |
285 | struct perf_cgroup_info *info; | 298 | struct perf_cgroup_info *info; |
286 | 299 | ||
287 | if (!task) | 300 | /* |
301 | * ctx->lock held by caller | ||
302 | * ensure we do not access cgroup data | ||
303 | * unless we have the cgroup pinned (css_get) | ||
304 | */ | ||
305 | if (!task || !ctx->nr_cgroups) | ||
288 | return; | 306 | return; |
289 | 307 | ||
290 | cgrp = perf_cgroup_from_task(task); | 308 | cgrp = perf_cgroup_from_task(task); |
291 | info = this_cpu_ptr(cgrp->info); | 309 | info = this_cpu_ptr(cgrp->info); |
292 | info->timestamp = now; | 310 | info->timestamp = ctx->timestamp; |
293 | } | 311 | } |
294 | 312 | ||
295 | #define PERF_CGROUP_SWOUT 0x1 /* cgroup switch out every event */ | 313 | #define PERF_CGROUP_SWOUT 0x1 /* cgroup switch out every event */ |
@@ -494,7 +512,8 @@ static inline int perf_cgroup_connect(pid_t pid, struct perf_event *event, | |||
494 | } | 512 | } |
495 | 513 | ||
496 | static inline void | 514 | static inline void |
497 | perf_cgroup_set_timestamp(struct task_struct *task, u64 now) | 515 | perf_cgroup_set_timestamp(struct task_struct *task, |
516 | struct perf_event_context *ctx) | ||
498 | { | 517 | { |
499 | } | 518 | } |
500 | 519 | ||
@@ -1613,7 +1632,7 @@ static int __perf_event_enable(void *info) | |||
1613 | /* | 1632 | /* |
1614 | * set current task's cgroup time reference point | 1633 | * set current task's cgroup time reference point |
1615 | */ | 1634 | */ |
1616 | perf_cgroup_set_timestamp(current, perf_clock()); | 1635 | perf_cgroup_set_timestamp(current, ctx); |
1617 | 1636 | ||
1618 | __perf_event_mark_enabled(event, ctx); | 1637 | __perf_event_mark_enabled(event, ctx); |
1619 | 1638 | ||
@@ -2048,7 +2067,7 @@ ctx_sched_in(struct perf_event_context *ctx, | |||
2048 | 2067 | ||
2049 | now = perf_clock(); | 2068 | now = perf_clock(); |
2050 | ctx->timestamp = now; | 2069 | ctx->timestamp = now; |
2051 | perf_cgroup_set_timestamp(task, now); | 2070 | perf_cgroup_set_timestamp(task, ctx); |
2052 | /* | 2071 | /* |
2053 | * First go through the list and put on any pinned groups | 2072 | * First go through the list and put on any pinned groups |
2054 | * in order to give them the best chance of going on. | 2073 | * in order to give them the best chance of going on. |
@@ -5795,7 +5814,6 @@ static void task_clock_event_read(struct perf_event *event) | |||
5795 | 5814 | ||
5796 | if (!in_nmi()) { | 5815 | if (!in_nmi()) { |
5797 | update_context_time(event->ctx); | 5816 | update_context_time(event->ctx); |
5798 | update_cgrp_time_from_event(event); | ||
5799 | time = event->ctx->time; | 5817 | time = event->ctx->time; |
5800 | } else { | 5818 | } else { |
5801 | u64 now = perf_clock(); | 5819 | u64 now = perf_clock(); |