aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/perf_event.c
diff options
context:
space:
mode:
authorMike Galbraith <efault@gmx.de>2010-03-26 06:11:33 -0400
committerIngo Molnar <mingo@elte.hu>2010-04-02 13:30:05 -0400
commit8bb39f9aa068262732fe44b965d7a6eb5a5a7d67 (patch)
tree820af25532c62179f518bda83ea27b6b21ee736b /kernel/perf_event.c
parent257ef9d21f1b008a6c7425544b36641c4325a922 (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.c15
1 files changed, 12 insertions, 3 deletions
diff --git a/kernel/perf_event.c b/kernel/perf_event.c
index b0feb4795af..96aae13c796 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
3400static int perf_event_task_match(struct perf_event *event) 3409static int perf_event_task_match(struct perf_event *event)