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>
This commit is contained in:
committed by
Ingo Molnar
parent
257ef9d21f
commit
8bb39f9aa0
@@ -3376,15 +3376,23 @@ static void perf_event_task_output(struct perf_event *event,
|
|||||||
struct perf_task_event *task_event)
|
struct perf_task_event *task_event)
|
||||||
{
|
{
|
||||||
struct perf_output_handle handle;
|
struct perf_output_handle handle;
|
||||||
int size;
|
|
||||||
struct task_struct *task = task_event->task;
|
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;
|
size = task_event->event_id.header.size;
|
||||||
ret = perf_output_begin(&handle, event, size, 0, 0);
|
ret = perf_output_begin(&handle, event, size, 0, 0);
|
||||||
|
|
||||||
if (ret)
|
if (ret) {
|
||||||
|
local_irq_restore(flags);
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
task_event->event_id.pid = perf_event_pid(event, task);
|
task_event->event_id.pid = perf_event_pid(event, task);
|
||||||
task_event->event_id.ppid = perf_event_pid(event, current);
|
task_event->event_id.ppid = perf_event_pid(event, current);
|
||||||
@@ -3395,6 +3403,7 @@ static void perf_event_task_output(struct perf_event *event,
|
|||||||
perf_output_put(&handle, task_event->event_id);
|
perf_output_put(&handle, task_event->event_id);
|
||||||
|
|
||||||
perf_output_end(&handle);
|
perf_output_end(&handle);
|
||||||
|
local_irq_restore(flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int perf_event_task_match(struct perf_event *event)
|
static int perf_event_task_match(struct perf_event *event)
|
||||||
|
Reference in New Issue
Block a user