Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8bb39f9a authored by Mike Galbraith's avatar Mike Galbraith Committed by Ingo Molnar
Browse files

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: default avatarLi Zefan <lizf@cn.fujitsu.com>
Signed-off-by: default avatarMike Galbraith <efault@gmx.de>
Tested-by: default avatarLi Zefan <lizf@cn.fujitsu.com>
Signed-off-by: default avatarPeter 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: default avatarIngo Molnar <mingo@elte.hu>
parent 257ef9d2
Loading
Loading
Loading
Loading
+12 −3
Original line number Diff line number Diff line
@@ -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);
@@ -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)