Skip to content

Commit

Permalink
perf: Fix 'perf sched record' deadlock
Browse files Browse the repository at this point in the history
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 <[email protected]>
Signed-off-by: Mike Galbraith <[email protected]>
Tested-by: Li Zefan <[email protected]>
Signed-off-by: Peter Zijlstra <[email protected]>
Cc: Arnaldo Carvalho de Melo <[email protected]>
Cc: Frederic Weisbecker <[email protected]>
LKML-Reference: <[email protected]>
Signed-off-by: Ingo Molnar <[email protected]>
  • Loading branch information
Mike Galbraith authored and Ingo Molnar committed Apr 2, 2010
1 parent 257ef9d commit 8bb39f9
Showing 1 changed file with 12 additions and 3 deletions.
15 changes: 12 additions & 3 deletions kernel/perf_event.c
Original file line number Diff line number Diff line change
Expand Up @@ -3376,15 +3376,23 @@ static void perf_event_task_output(struct perf_event *event,
struct perf_task_event *task_event)
{
struct perf_output_handle handle;
int size;
struct task_struct *task = task_event->task;
int ret;
unsigned long flags;
int size, ret;

/*
* If this CPU attempts to acquire an rq lock held by a CPU spinning
* in perf_output_lock() from interrupt context, it's game over.
*/
local_irq_save(flags);

size = task_event->event_id.header.size;
ret = perf_output_begin(&handle, event, size, 0, 0);

if (ret)
if (ret) {
local_irq_restore(flags);
return;
}

task_event->event_id.pid = perf_event_pid(event, task);
task_event->event_id.ppid = perf_event_pid(event, current);
Expand All @@ -3395,6 +3403,7 @@ static void perf_event_task_output(struct perf_event *event,
perf_output_put(&handle, task_event->event_id);

perf_output_end(&handle);
local_irq_restore(flags);
}

static int perf_event_task_match(struct perf_event *event)
Expand Down

0 comments on commit 8bb39f9

Please sign in to comment.