diff options
| author | Peter Zijlstra <[email protected]> | 2016-02-24 18:45:46 +0100 |
|---|---|---|
| committer | Ingo Molnar <[email protected]> | 2016-02-25 08:42:34 +0100 |
| commit | 28a967c3a2f99fa3b5f762f25cb2a319d933571b (patch) | |
| tree | a3fedc2b7321749e94a6ad9b4ff374147e28ec8e | |
| parent | 9107c89e269d2738019861bb518e3d59bef01781 (diff) | |
perf: Cure event->pending_disable race
Because event_sched_out() checks event->pending_disable _before_
actually disabling the event, it can happen that the event fires after
it checks but before it gets disabled.
This would leave event->pending_disable set and the queued irq_work
will try and process it.
However, if the event trigger was during schedule(), the event might
have been de-scheduled by the time the irq_work runs, and
perf_event_disable_local() will fail.
Fix this by checking event->pending_disable _after_ we call
event->pmu->del(). This depends on the latter being a compiler
barrier, such that the compiler does not lift the load and re-creates
the problem.
Tested-by: Alexander Shishkin <[email protected]>
Signed-off-by: Peter Zijlstra (Intel) <[email protected]>
Reviewed-by: Alexander Shishkin <[email protected]>
Cc: Arnaldo Carvalho de Melo <[email protected]>
Cc: Jiri Olsa <[email protected]>
Cc: Linus Torvalds <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Cc: Thomas Gleixner <[email protected]>
Cc: [email protected]
Cc: [email protected]
Cc: [email protected]
Cc: [email protected]
Cc: [email protected]
Cc: [email protected]
Link: http://lkml.kernel.org/r/[email protected]
Signed-off-by: Ingo Molnar <[email protected]>
| -rw-r--r-- | kernel/events/core.c | 6 |
1 files changed, 3 insertions, 3 deletions
diff --git a/kernel/events/core.c b/kernel/events/core.c index ea064ca8dd3c..de14b67a3b0b 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -1696,14 +1696,14 @@ event_sched_out(struct perf_event *event, perf_pmu_disable(event->pmu); + event->tstamp_stopped = tstamp; + event->pmu->del(event, 0); + event->oncpu = -1; event->state = PERF_EVENT_STATE_INACTIVE; if (event->pending_disable) { event->pending_disable = 0; event->state = PERF_EVENT_STATE_OFF; } - event->tstamp_stopped = tstamp; - event->pmu->del(event, 0); - event->oncpu = -1; if (!is_software_event(event)) cpuctx->active_oncpu--; |