Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1759393Ab3GaJGh (ORCPT ); Wed, 31 Jul 2013 05:06:37 -0400 Received: from lgeamrelo01.lge.com ([156.147.1.125]:55192 "EHLO LGEAMRELO01.lge.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1755043Ab3GaJDk (ORCPT ); Wed, 31 Jul 2013 05:03:40 -0400 X-AuditID: 9c93017d-b7b45ae000000e34-1c-51f8d2e4a1d8 From: Namhyung Kim To: Steven Rostedt Cc: Namhyung Kim , Hyeoncheol Lee , LKML , Masami Hiramatsu , Srikar Dronamraju , Oleg Nesterov , "zhangwei(Jovi)" , Arnaldo Carvalho de Melo Subject: [PATCH 08/13] tracing/uprobes: Fetch args before reserving a ring buffer Date: Wed, 31 Jul 2013 18:03:25 +0900 Message-Id: <1375261410-11219-9-git-send-email-namhyung@kernel.org> X-Mailer: git-send-email 1.7.11.7 In-Reply-To: <1375261410-11219-1-git-send-email-namhyung@kernel.org> References: <1375261410-11219-1-git-send-email-namhyung@kernel.org> X-Brightmail-Tracker: AAAAAA== Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 4339 Lines: 142 From: Namhyung Kim 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 Cc: Srikar Dronamraju Cc: Oleg Nesterov Cc: zhangwei(Jovi) Cc: Arnaldo Carvalho de Melo Signed-off-by: Namhyung Kim --- kernel/trace/trace_uprobe.c | 69 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 53 insertions(+), 16 deletions(-) diff --git a/kernel/trace/trace_uprobe.c b/kernel/trace/trace_uprobe.c index bcc7bd300c99..f78098398a61 100644 --- a/kernel/trace/trace_uprobe.c +++ b/kernel/trace/trace_uprobe.c @@ -516,15 +516,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)) { @@ -536,13 +552,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 */ @@ -756,11 +771,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); + 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 +814,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 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/