mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Namhyung Kim <namhyung@kernel.org>
To: Steven Rostedt <rostedt@goodmis.org>, Oleg Nesterov <oleg@redhat.com>
Cc: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>,
	Srikar Dronamraju <srikar@linux.vnet.ibm.com>,
	Hyeoncheol Lee <cheol.lee@lge.com>,
	"zhangwei(Jovi)" <jovi.zhangwei@huawei.com>,
	Arnaldo Carvalho de Melo <acme@ghostprotocols.net>,
	Hemant Kumar <hkshaw@linux.vnet.ibm.com>,
	LKML <linux-kernel@vger.kernel.org>,
	Namhyung Kim <namhyung.kim@lge.com>
Subject: [PATCH 14/17] tracing/uprobes: Fetch args before reserving a ring buffer
Date: Mon,  9 Dec 2013 15:20:02 +0900	[thread overview]
Message-ID: <1386570005-3368-15-git-send-email-namhyung@kernel.org> (raw)
In-Reply-To: <1386570005-3368-1-git-send-email-namhyung@kernel.org>

From: Namhyung Kim <namhyung.kim@lge.com>

Fetching from user space should be done in a non-atomic context.  So
use a per-cpu buffer and copy its content to the ring buffer
atomically.  Note that we can migrate during accessing user memory
thus use a per-cpu mutex to protect concurrent accesses.

This is needed since we'll be able to fetch args from an user memory
which can be swapped out.  Before that uprobes could fetch args from
registers only which saved in a kernel space.

While at it, use __get_data_size() and store_trace_args() to reduce
code duplication.  And add struct uprobe_cpu_buffer and its helpers as
suggested by Oleg.

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

diff --git a/kernel/trace/trace_uprobe.c b/kernel/trace/trace_uprobe.c
index d407a556aa55..f86a6a711de9 100644
--- a/kernel/trace/trace_uprobe.c
+++ b/kernel/trace/trace_uprobe.c
@@ -650,21 +650,117 @@ static const struct file_operations uprobe_profile_ops = {
 	.release	= seq_release,
 };
 
+struct uprobe_cpu_buffer {
+	struct mutex mutex;
+	void *buf;
+};
+static struct uprobe_cpu_buffer __percpu *uprobe_cpu_buffer;
+static int uprobe_buffer_refcnt;
+
+static int uprobe_buffer_init(void)
+{
+	int cpu, err_cpu;
+
+	uprobe_cpu_buffer = alloc_percpu(struct uprobe_cpu_buffer);
+	if (uprobe_cpu_buffer == NULL)
+		return -ENOMEM;
+
+	for_each_possible_cpu(cpu) {
+		struct page *p = alloc_pages_node(cpu_to_node(cpu),
+						  GFP_KERNEL, 0);
+		if (p == NULL) {
+			err_cpu = cpu;
+			goto err;
+		}
+		per_cpu_ptr(uprobe_cpu_buffer, cpu)->buf = page_address(p);
+		mutex_init(&per_cpu_ptr(uprobe_cpu_buffer, cpu)->mutex);
+	}
+
+	return 0;
+
+err:
+	for_each_possible_cpu(cpu) {
+		if (cpu == err_cpu)
+			break;
+		free_page((unsigned long)per_cpu_ptr(uprobe_cpu_buffer, cpu)->buf);
+	}
+
+	free_percpu(uprobe_cpu_buffer);
+	return -ENOMEM;
+}
+
+static int uprobe_buffer_enable(void)
+{
+	int ret = 0;
+
+	BUG_ON(!mutex_is_locked(&event_mutex));
+
+	if (uprobe_buffer_refcnt++ == 0) {
+		ret = uprobe_buffer_init();
+		if (ret < 0)
+			uprobe_buffer_refcnt--;
+	}
+
+	return ret;
+}
+
+static void uprobe_buffer_disable(void)
+{
+	BUG_ON(!mutex_is_locked(&event_mutex));
+
+	if (--uprobe_buffer_refcnt == 0) {
+		free_percpu(uprobe_cpu_buffer);
+		uprobe_cpu_buffer = NULL;
+	}
+}
+
+static struct uprobe_cpu_buffer *uprobe_buffer_get(void)
+{
+	struct uprobe_cpu_buffer *ucb;
+	int cpu;
+
+	cpu = raw_smp_processor_id();
+	ucb = per_cpu_ptr(uprobe_cpu_buffer, cpu);
+
+	/*
+	 * Use per-cpu buffers for fastest access, but we might migrate
+	 * so the mutex makes sure we have sole access to it.
+	 */
+	mutex_lock(&ucb->mutex);
+
+	return ucb;
+}
+
+static void uprobe_buffer_put(struct uprobe_cpu_buffer *ucb)
+{
+	mutex_unlock(&ucb->mutex);
+}
+
 static void uprobe_trace_print(struct trace_uprobe *tu,
 				unsigned long func, struct pt_regs *regs)
 {
 	struct uprobe_trace_entry_head *entry;
 	struct ring_buffer_event *event;
 	struct ring_buffer *buffer;
+	struct uprobe_cpu_buffer *ucb;
 	void *data;
-	int size, i;
+	int size, dsize, esize;
 	struct ftrace_event_call *call = &tu->tp.call;
 
-	size = SIZEOF_TRACE_ENTRY(is_ret_probe(tu));
+	dsize = __get_data_size(&tu->tp, regs);
+	esize = SIZEOF_TRACE_ENTRY(is_ret_probe(tu));
+
+	if (WARN_ON_ONCE(!uprobe_cpu_buffer || tu->tp.size + dsize > PAGE_SIZE))
+		return;
+
+	ucb = uprobe_buffer_get();
+	store_trace_args(esize, &tu->tp, regs, ucb->buf, dsize);
+
+	size = esize + tu->tp.size + dsize;
 	event = trace_current_buffer_lock_reserve(&buffer, call->event.type,
-						  size + tu->tp.size, 0, 0);
+						  size, 0, 0);
 	if (!event)
-		return;
+		goto out;
 
 	entry = ring_buffer_event_data(event);
 	if (is_ret_probe(tu)) {
@@ -676,13 +772,13 @@ static void uprobe_trace_print(struct trace_uprobe *tu,
 		data = DATAOF_TRACE_ENTRY(entry, false);
 	}
 
-	for (i = 0; i < tu->tp.nr_args; i++) {
-		call_fetch(&tu->tp.args[i].fetch, regs,
-			   data + tu->tp.args[i].offset);
-	}
+	memcpy(data, ucb->buf, tu->tp.size + dsize);
 
 	if (!call_filter_check_discard(call, entry, buffer, event))
 		trace_buffer_unlock_commit(buffer, event, 0, 0);
+
+out:
+	uprobe_buffer_put(ucb);
 }
 
 /* uprobe handler */
@@ -750,6 +846,10 @@ probe_event_enable(struct trace_uprobe *tu, int flag, filter_func_t filter)
 	if (trace_probe_is_enabled(&tu->tp))
 		return -EINTR;
 
+	ret = uprobe_buffer_enable();
+	if (ret < 0)
+		return ret;
+
 	WARN_ON(!uprobe_filter_is_empty(&tu->filter));
 
 	tu->tp.flags |= flag;
@@ -770,6 +870,8 @@ static void probe_event_disable(struct trace_uprobe *tu, int flag)
 
 	uprobe_unregister(tu->inode, tu->offset, &tu->consumer);
 	tu->tp.flags &= ~flag;
+
+	uprobe_buffer_disable();
 }
 
 static int uprobe_event_define_fields(struct ftrace_event_call *event_call)
@@ -896,11 +998,24 @@ static void uprobe_perf_print(struct trace_uprobe *tu,
 	struct ftrace_event_call *call = &tu->tp.call;
 	struct uprobe_trace_entry_head *entry;
 	struct hlist_head *head;
+	struct uprobe_cpu_buffer *ucb;
 	void *data;
-	int size, rctx, i;
+	int size, dsize, esize;
+	int rctx;
+
+	dsize = __get_data_size(&tu->tp, regs);
+	esize = SIZEOF_TRACE_ENTRY(is_ret_probe(tu));
 
-	size = SIZEOF_TRACE_ENTRY(is_ret_probe(tu));
-	size = ALIGN(size + tu->tp.size + sizeof(u32), sizeof(u64)) - sizeof(u32);
+	if (WARN_ON_ONCE(!uprobe_cpu_buffer))
+		return;
+
+	size = esize + tu->tp.size + dsize;
+	size = ALIGN(size + sizeof(u32), sizeof(u64)) - sizeof(u32);
+	if (WARN_ONCE(size > PERF_MAX_TRACE_SIZE, "profile buffer not large enough"))
+		return;
+
+	ucb = uprobe_buffer_get();
+	store_trace_args(esize, &tu->tp, regs, ucb->buf, dsize);
 
 	preempt_disable();
 	head = this_cpu_ptr(call->perf_events);
@@ -920,15 +1035,18 @@ static void uprobe_perf_print(struct trace_uprobe *tu,
 		data = DATAOF_TRACE_ENTRY(entry, false);
 	}
 
-	for (i = 0; i < tu->tp.nr_args; i++) {
-		struct probe_arg *parg = &tu->tp.args[i];
+	memcpy(data, ucb->buf, tu->tp.size + dsize);
+
+	if (size - esize > tu->tp.size + dsize) {
+		int len = tu->tp.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();
+	uprobe_buffer_put(ucb);
 }
 
 /* uprobe profile handler */
-- 
1.7.11.7


  parent reply	other threads:[~2013-12-09  6:20 UTC|newest]

Thread overview: 41+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2013-12-09  6:19 [PATCHSET 00/17] tracing/uprobes: Add support for more fetch methods (v8) Namhyung Kim
2013-12-09  6:19 ` [PATCH 01/17] tracing/uprobes: Fix documentation of uprobe registration syntax Namhyung Kim
2013-12-09  6:19 ` [PATCH 02/17] tracing/probes: Fix basic print type functions Namhyung Kim
2013-12-09  6:19 ` [PATCH 03/17] tracing/kprobes: Factor out struct trace_probe Namhyung Kim
2013-12-09  6:19 ` [PATCH 04/17] tracing/uprobes: Convert to " Namhyung Kim
2013-12-09  6:19 ` [PATCH 05/17] tracing/kprobes: Move common functions to trace_probe.h Namhyung Kim
2013-12-09  6:19 ` [PATCH 06/17] tracing/probes: Integrate duplicate set_print_fmt() Namhyung Kim
2013-12-09  6:19 ` [PATCH 07/17] tracing/probes: Move fetch function helpers to trace_probe.h Namhyung Kim
2013-12-09 15:12   ` Masami Hiramatsu
2013-12-09  6:19 ` [PATCH 08/17] tracing/probes: Split [ku]probes_fetch_type_table Namhyung Kim
2013-12-09 15:09   ` Masami Hiramatsu
2013-12-10  1:05     ` Namhyung Kim
2013-12-10  4:41       ` Namhyung Kim
2013-12-10  9:14         ` Masami Hiramatsu
2013-12-09  6:19 ` [PATCH 09/17] tracing/probes: Implement 'stack' fetch method for uprobes Namhyung Kim
2013-12-09  6:19 ` [PATCH 10/17] tracing/probes: Move 'symbol' fetch method to kprobes Namhyung Kim
2013-12-10 10:12   ` Masami Hiramatsu
2013-12-11  1:05     ` Namhyung Kim
2013-12-11  1:26       ` Masami Hiramatsu
2013-12-09  6:19 ` [PATCH 11/17] tracing/probes: Add fetch{,_size} member into deref fetch method Namhyung Kim
2013-12-09  6:20 ` [PATCH 12/17] tracing/probes: Implement 'memory' fetch method for uprobes Namhyung Kim
2013-12-10 11:00   ` Masami Hiramatsu
2013-12-11  1:15     ` Namhyung Kim
2013-12-11  1:27       ` Masami Hiramatsu
2013-12-09  6:20 ` [PATCH 13/17] tracing/uprobes: Pass 'is_return' to traceprobe_parse_probe_arg() Namhyung Kim
2013-12-09 15:11   ` Masami Hiramatsu
2013-12-09  6:20 ` Namhyung Kim [this message]
2013-12-09  6:20 ` [PATCH 15/17] tracing/uprobes: Add support for full argument access methods Namhyung Kim
2013-12-09  6:20 ` [PATCH 16/17] uprobes: Allocate ->utask before handler_chain() for tracing handlers Namhyung Kim
2013-12-10 10:41   ` Masami Hiramatsu
2013-12-10 15:57     ` Oleg Nesterov
2013-12-11  1:30       ` Namhyung Kim
2013-12-11  1:43       ` Masami Hiramatsu
2013-12-11 18:11         ` Oleg Nesterov
2013-12-12  5:55           ` Masami Hiramatsu
2013-12-12 19:46             ` Oleg Nesterov
2013-12-13  1:57               ` Masami Hiramatsu
2013-12-13  1:58   ` Masami Hiramatsu
2013-12-09  6:20 ` [PATCH 17/17] tracing/uprobes: Add @+file_offset fetch method Namhyung Kim
  -- strict thread matches above, loose matches on Subject: below --
2013-12-16  4:31 [PATCHSET 00/17] tracing/uprobes: Add support for more fetch methods (v9) Namhyung Kim
2013-12-16  4:32 ` [PATCH 14/17] tracing/uprobes: Fetch args before reserving a ring buffer Namhyung Kim
2013-11-27  6:19 [PATCHSET 00/17] tracing/uprobes: Add support for more fetch methods (v7) Namhyung Kim
2013-11-27  6:20 ` [PATCH 14/17] tracing/uprobes: Fetch args before reserving a ring buffer Namhyung Kim

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1386570005-3368-15-git-send-email-namhyung@kernel.org \
    --to=namhyung@kernel.org \
    --cc=acme@ghostprotocols.net \
    --cc=cheol.lee@lge.com \
    --cc=hkshaw@linux.vnet.ibm.com \
    --cc=jovi.zhangwei@huawei.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=masami.hiramatsu.pt@hitachi.com \
    --cc=namhyung.kim@lge.com \
    --cc=oleg@redhat.com \
    --cc=rostedt@goodmis.org \
    --cc=srikar@linux.vnet.ibm.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

Powered by JetHome