lkml.org 
[lkml]   [2013]   [Jul]   [3]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 08/12] tracing/uprobes: Fetch args before reserving a ring buffer
    Date
    From: Namhyung Kim <namhyung.kim@lge.com>

    Fetching from user space should be done in a non-atomic context. So
    use a temporary buffer and copy its content to the ring buffer
    atomically.

    While at it, use __get_data_size() and store_trace_args() to reduce
    code duplication.

    Cc: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
    Cc: Srikar Dronamraju <srikar@linux.vnet.ibm.com>
    Cc: Oleg Nesterov <oleg@redhat.com>
    Cc: Arnaldo Carvalho de Melo <acme@ghostprotocols.net>
    Signed-off-by: Namhyung Kim <namhyung@kernel.org>
    ---
    kernel/trace/trace_uprobe.c | 69 ++++++++++++++++++++++++++++++++++-----------
    1 file changed, 52 insertions(+), 17 deletions(-)

    diff --git a/kernel/trace/trace_uprobe.c b/kernel/trace/trace_uprobe.c
    index e4e294d61bec..8611b3289393 100644
    --- a/kernel/trace/trace_uprobe.c
    +++ b/kernel/trace/trace_uprobe.c
    @@ -514,15 +514,31 @@ static void uprobe_trace_print(struct trace_uprobe *tu,
    struct uprobe_trace_entry_head *entry;
    struct ring_buffer_event *event;
    struct ring_buffer *buffer;
    - void *data;
    - int size, i;
    + void *data, *tmp;
    + int size, dsize, esize;
    struct ftrace_event_call *call = &tu->p.call;

    - size = SIZEOF_TRACE_ENTRY(is_ret_probe(tu));
    + dsize = __get_data_size(&tu->p, regs);
    + esize = SIZEOF_TRACE_ENTRY(is_ret_probe(tu));
    +
    + /*
    + * A temporary buffer is used for storing fetched data before reserving
    + * the ring buffer because fetching from user space should be done in a
    + * non-atomic context.
    + */
    + tmp = kmalloc(tu->p.size + dsize, GFP_KERNEL);
    + if (tmp == NULL)
    + return;
    +
    + store_trace_args(esize, &tu->p, regs, tmp, dsize);
    +
    + size = esize + tu->p.size + dsize;
    event = trace_current_buffer_lock_reserve(&buffer, call->event.type,
    - size + tu->p.size, 0, 0);
    - if (!event)
    + size, 0, 0);
    + if (!event) {
    + kfree(tmp);
    return;
    + }

    entry = ring_buffer_event_data(event);
    if (is_ret_probe(tu)) {
    @@ -534,13 +550,12 @@ static void uprobe_trace_print(struct trace_uprobe *tu,
    data = DATAOF_TRACE_ENTRY(entry, false);
    }

    - for (i = 0; i < tu->p.nr_args; i++) {
    - call_fetch(&tu->p.args[i].fetch, regs,
    - data + tu->p.args[i].offset);
    - }
    + memcpy(data, tmp, tu->p.size + dsize);

    if (!filter_current_check_discard(buffer, call, entry, event))
    trace_buffer_unlock_commit(buffer, event, 0, 0);
    +
    + kfree(tmp);
    }

    /* uprobe handler */
    @@ -754,13 +769,30 @@ static void uprobe_perf_print(struct trace_uprobe *tu,
    struct ftrace_event_call *call = &tu->p.call;
    struct uprobe_trace_entry_head *entry;
    struct hlist_head *head;
    - void *data;
    - int size, rctx, i;
    + void *data, *tmp;
    + int size, dsize, esize;
    + int rctx;
    +
    + dsize = __get_data_size(&tu->p, regs);
    + esize = SIZEOF_TRACE_ENTRY(is_ret_probe(tu));
    +
    + /*
    + * A temporary buffer is used for storing fetched data before reserving
    + * the ring buffer because fetching from user space should be done in a
    + * non-atomic context.
    + */
    + tmp = kmalloc(tu->p.size + dsize, GFP_KERNEL);
    + if (tmp == NULL)
    + return;

    - size = SIZEOF_TRACE_ENTRY(is_ret_probe(tu));
    - size = ALIGN(size + tu->p.size + sizeof(u32), sizeof(u64)) - sizeof(u32);
    - if (WARN_ONCE(size > PERF_MAX_TRACE_SIZE, "profile buffer not large enough"))
    + store_trace_args(esize, &tu->p, regs, tmp, dsize);
    +
    + size = esize + tu->p.size + dsize;
    + size = ALIGN(size + + sizeof(u32), sizeof(u64)) - sizeof(u32);
    + if (WARN_ONCE(size > PERF_MAX_TRACE_SIZE, "profile buffer not large enough")) {
    + kfree(tmp);
    return;
    + }

    preempt_disable();
    head = this_cpu_ptr(call->perf_events);
    @@ -780,15 +812,18 @@ static void uprobe_perf_print(struct trace_uprobe *tu,
    data = DATAOF_TRACE_ENTRY(entry, false);
    }

    - for (i = 0; i < tu->p.nr_args; i++) {
    - struct probe_arg *parg = &tu->p.args[i];
    + memcpy(data, tmp, tu->p.size + dsize);
    +
    + if (size - esize > tu->p.size + dsize) {
    + int len = tu->p.size + dsize;

    - call_fetch(&parg->fetch, regs, data + parg->offset);
    + memset(data + len, 0, size - esize - len);
    }

    perf_trace_buf_submit(entry, size, rctx, 0, 1, regs, head, NULL);
    out:
    preempt_enable();
    + kfree(tmp);
    }

    /* uprobe profile handler */
    --
    1.7.11.7


    \
     
     \ /
      Last update: 2013-07-03 16:21    [W:4.973 / U:0.132 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site