diff options
author | Peter Zijlstra <peterz@infradead.org> | 2009-08-13 03:51:55 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2009-08-13 06:18:43 -0400 |
commit | bcfc2602e8541ac13b1def38e2591dca072cff7a (patch) | |
tree | 0e5601dac2fbc4ca1ff452cebd1adb345b710599 /kernel | |
parent | 8fd101f20bdf771949a8f3a5a779877d09b2fb56 (diff) |
perf_counter: Fix swcounter context invariance
perf_swcounter_is_counting() uses a lock, which means we cannot
use swcounters from NMI or when holding that particular lock,
this is unintended.
The below removes the lock, this opens up race window, but not
worse than the swcounters already experience due to RCU
traversal of the context in perf_swcounter_ctx_event().
This also fixes the hard lockups while opening a lockdep
tracepoint counter.
Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl>
Acked-by: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Paul Mackerras <paulus@samba.org>
Cc: stephane eranian <eranian@googlemail.com>
Cc: Corey J Ashford <cjashfor@us.ibm.com>
LKML-Reference: <1250149915.10001.66.camel@twins>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/perf_counter.c | 44 |
1 files changed, 18 insertions, 26 deletions
diff --git a/kernel/perf_counter.c b/kernel/perf_counter.c index e26d2fcfa320..3dd4339589a0 100644 --- a/kernel/perf_counter.c +++ b/kernel/perf_counter.c | |||
@@ -3444,40 +3444,32 @@ static void perf_swcounter_add(struct perf_counter *counter, u64 nr, | |||
3444 | 3444 | ||
3445 | static int perf_swcounter_is_counting(struct perf_counter *counter) | 3445 | static int perf_swcounter_is_counting(struct perf_counter *counter) |
3446 | { | 3446 | { |
3447 | struct perf_counter_context *ctx; | 3447 | /* |
3448 | unsigned long flags; | 3448 | * The counter is active, we're good! |
3449 | int count; | 3449 | */ |
3450 | |||
3451 | if (counter->state == PERF_COUNTER_STATE_ACTIVE) | 3450 | if (counter->state == PERF_COUNTER_STATE_ACTIVE) |
3452 | return 1; | 3451 | return 1; |
3453 | 3452 | ||
3453 | /* | ||
3454 | * The counter is off/error, not counting. | ||
3455 | */ | ||
3454 | if (counter->state != PERF_COUNTER_STATE_INACTIVE) | 3456 | if (counter->state != PERF_COUNTER_STATE_INACTIVE) |
3455 | return 0; | 3457 | return 0; |
3456 | 3458 | ||
3457 | /* | 3459 | /* |
3458 | * If the counter is inactive, it could be just because | 3460 | * The counter is inactive, if the context is active |
3459 | * its task is scheduled out, or because it's in a group | 3461 | * we're part of a group that didn't make it on the 'pmu', |
3460 | * which could not go on the PMU. We want to count in | 3462 | * not counting. |
3461 | * the first case but not the second. If the context is | ||
3462 | * currently active then an inactive software counter must | ||
3463 | * be the second case. If it's not currently active then | ||
3464 | * we need to know whether the counter was active when the | ||
3465 | * context was last active, which we can determine by | ||
3466 | * comparing counter->tstamp_stopped with ctx->time. | ||
3467 | * | ||
3468 | * We are within an RCU read-side critical section, | ||
3469 | * which protects the existence of *ctx. | ||
3470 | */ | 3463 | */ |
3471 | ctx = counter->ctx; | 3464 | if (counter->ctx->is_active) |
3472 | spin_lock_irqsave(&ctx->lock, flags); | 3465 | return 0; |
3473 | count = 1; | 3466 | |
3474 | /* Re-check state now we have the lock */ | 3467 | /* |
3475 | if (counter->state < PERF_COUNTER_STATE_INACTIVE || | 3468 | * We're inactive and the context is too, this means the |
3476 | counter->ctx->is_active || | 3469 | * task is scheduled out, we're counting events that happen |
3477 | counter->tstamp_stopped < ctx->time) | 3470 | * to us, like migration events. |
3478 | count = 0; | 3471 | */ |
3479 | spin_unlock_irqrestore(&ctx->lock, flags); | 3472 | return 1; |
3480 | return count; | ||
3481 | } | 3473 | } |
3482 | 3474 | ||
3483 | static int perf_swcounter_match(struct perf_counter *counter, | 3475 | static int perf_swcounter_match(struct perf_counter *counter, |