diff options
author | Mike Galbraith <efault@gmx.de> | 2010-03-26 06:11:33 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2010-04-02 13:30:05 -0400 |
commit | 8bb39f9aa068262732fe44b965d7a6eb5a5a7d67 (patch) | |
tree | 820af25532c62179f518bda83ea27b6b21ee736b /kernel/perf_event.c | |
parent | 257ef9d21f1b008a6c7425544b36641c4325a922 (diff) |
perf: Fix 'perf sched record' deadlock
perf sched record can deadlock a box should the holder of
handle->data->lock take an interrupt, and then attempt to
acquire an rq lock held by a CPU trying to acquire the
same lock. Disable interrupts.
CPU0 CPU1
sched event with rq->lock held
grab handle->data->lock
spin on handle->data->lock
interrupt
try to grab rq->lock
Reported-by: Li Zefan <lizf@cn.fujitsu.com>
Signed-off-by: Mike Galbraith <efault@gmx.de>
Tested-by: Li Zefan <lizf@cn.fujitsu.com>
Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
LKML-Reference: <1269598293.6174.8.camel@marge.simson.net>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'kernel/perf_event.c')
-rw-r--r-- | kernel/perf_event.c | 15 |
1 files changed, 12 insertions, 3 deletions
diff --git a/kernel/perf_event.c b/kernel/perf_event.c index b0feb4795af3..96aae13c7960 100644 --- a/kernel/perf_event.c +++ b/kernel/perf_event.c | |||
@@ -3376,15 +3376,23 @@ static void perf_event_task_output(struct perf_event *event, | |||
3376 | struct perf_task_event *task_event) | 3376 | struct perf_task_event *task_event) |
3377 | { | 3377 | { |
3378 | struct perf_output_handle handle; | 3378 | struct perf_output_handle handle; |
3379 | int size; | ||
3380 | struct task_struct *task = task_event->task; | 3379 | struct task_struct *task = task_event->task; |
3381 | int ret; | 3380 | unsigned long flags; |
3381 | int size, ret; | ||
3382 | |||
3383 | /* | ||
3384 | * If this CPU attempts to acquire an rq lock held by a CPU spinning | ||
3385 | * in perf_output_lock() from interrupt context, it's game over. | ||
3386 | */ | ||
3387 | local_irq_save(flags); | ||
3382 | 3388 | ||
3383 | size = task_event->event_id.header.size; | 3389 | size = task_event->event_id.header.size; |
3384 | ret = perf_output_begin(&handle, event, size, 0, 0); | 3390 | ret = perf_output_begin(&handle, event, size, 0, 0); |
3385 | 3391 | ||
3386 | if (ret) | 3392 | if (ret) { |
3393 | local_irq_restore(flags); | ||
3387 | return; | 3394 | return; |
3395 | } | ||
3388 | 3396 | ||
3389 | task_event->event_id.pid = perf_event_pid(event, task); | 3397 | task_event->event_id.pid = perf_event_pid(event, task); |
3390 | task_event->event_id.ppid = perf_event_pid(event, current); | 3398 | task_event->event_id.ppid = perf_event_pid(event, current); |
@@ -3395,6 +3403,7 @@ static void perf_event_task_output(struct perf_event *event, | |||
3395 | perf_output_put(&handle, task_event->event_id); | 3403 | perf_output_put(&handle, task_event->event_id); |
3396 | 3404 | ||
3397 | perf_output_end(&handle); | 3405 | perf_output_end(&handle); |
3406 | local_irq_restore(flags); | ||
3398 | } | 3407 | } |
3399 | 3408 | ||
3400 | static int perf_event_task_match(struct perf_event *event) | 3409 | static int perf_event_task_match(struct perf_event *event) |