lkml.org 
[lkml]   [2010]   [Apr]   [2]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    Date
    From
    Subject[tip:perf/core] perf: Fix 'perf sched record' deadlock
    Commit-ID:  8bb39f9aa068262732fe44b965d7a6eb5a5a7d67
    Gitweb: http://git.kernel.org/tip/8bb39f9aa068262732fe44b965d7a6eb5a5a7d67
    Author: Mike Galbraith <efault@gmx.de>
    AuthorDate: Fri, 26 Mar 2010 11:11:33 +0100
    Committer: Ingo Molnar <mingo@elte.hu>
    CommitDate: Fri, 2 Apr 2010 19:30:05 +0200

    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>
    ---
    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 b0feb47..96aae13 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,
    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)

    \
     
     \ /
      Last update: 2010-04-02 21:11    [W:0.031 / U:0.072 seconds]
    ©2003-2016 Jasper Spaans. hosted at Digital OceanAdvertise on this site