Skip to contentSkip to conversation

ftrace perf: Fixes and speedup

PATCH In review Jiri Olsa 6 participants 22 messages Mar 16, 2016 – Mar 31, 2016

Subsystems (3)
lore
Share & tools
lore

Jiri Olsa

author reviewer
#0 lore raw

v2 cover ftrace perf: Fixes and speedup

hi,
sending some small perf related fixes plus one change to speedup ftrace:function perf registration.

v2 changes:
- added comment requested by Steven
- changed patch 1 according to Namhyung's suggestion

Also available in here:
git://git.kernel.org/pub/scm/linux/kernel/git/jolsa/perf.git
ftrace/fixes

thanks,
jirka
---
Jiri Olsa (5):
ftrace perf: Check sample types only for sampling events ftrace perf: Move exclude_kernel tracepoint check to init event ftrace perf: Use ftrace_ops::private to store event pointer
ftrace: Make ftrace_hash_rec_enable return update bool
ftrace: Update dynamic ftrace calls only if necessary

 include/linux/perf_event.h      |  3 +++
 kernel/events/core.c            | 27 ++++++++++++++++++++++-----
 kernel/trace/ftrace.c           | 37 ++++++++++++++++++++++---------------
 kernel/trace/trace_event_perf.c | 38 +++++++++++++++++++++++++++++++-------
 4 files changed, 78 insertions(+), 27 deletions(-)
author reviewer
#1 lore raw

PATCH v2 1/5 ftrace perf: Check sample types only for sampling events

Currently we check sample type for ftrace:function event even if it's not created as sampling event. That prevents creating ftrace_function event in counting mode.

Making sure we check sample types only for sampling events.

Before:

  $ sudo perf stat -e ftrace:function ls
  ...

Performance counter stats for 'ls':

<not supported> ftrace:function

0.001983662 seconds time elapsed

After:

  $ sudo perf stat -e ftrace:function ls
  ...

Performance counter stats for 'ls':

              44,498      ftrace:function

         0.037534722 seconds time elapsed
---
 kernel/trace/trace_event_perf.c | 3 +++
 1 file changed, 3 insertions(+)
kernel/trace/trace_event_perf.c +3 -0
index 00df25fd86ef..e11108f1d197 100644
@@ -47,6 +47,9 @@ static int perf_trace_event_perm(struct trace_event_call *tp_event,
 		if (perf_paranoid_tracepoint_raw() && !capable(CAP_SYS_ADMIN))
 			return -EPERM;
 
+		if (!is_sampling_event(p_event))
+			return 0;
+
 		/*
 		 * We don't allow user space callchains for  function trace
 		 * event, due to issues with page faults while tracing page
Signature · 1 line
2.4.3
maintainer Acked-by on v2 1/5
#2 lore raw

Re: [PATCH 1/5] ftrace perf: Check sample types only for sampling events

Quoted 20 lines · On Wed, 16 Mar 2016 15:34:29 +0100 Jiri Olsa <[email protected]> wrote:

Currently we check sample type for ftrace:function event even if it's not created as sampling event. That prevents creating ftrace_function event in counting mode.

Making sure we check sample types only for sampling events.

Before:

  $ sudo perf stat -e ftrace:function ls
  ...

Performance counter stats for 'ls':

<not supported> ftrace:function

0.001983662 seconds time elapsed

After:

  $ sudo perf stat -e ftrace:function ls
  ...
Quoted 8 lines
   Performance counter stats for 'ls':

              44,498      ftrace:function

         0.037534722 seconds time elapsed

-- Steve

#3 lore raw

[tip:perf/core] ftrace/perf: Check sample types only for sampling events

In reply to Jiri Olsa
ftrace/perf: Check sample types only for sampling events
Commit-ID:  0a74c5b3d20d2a8693848b6ae4f1a97624f5b781
Gitweb:     http://git.kernel.org/tip/0a74c5b3d20d2a8693848b6ae4f1a97624f5b781
Author:     Jiri Olsa <[email protected]>
AuthorDate: Wed, 16 Mar 2016 15:34:29 +0100
Committer:  Ingo Molnar <[email protected]>
CommitDate: Thu, 31 Mar 2016 10:30:45 +0200

ftrace/perf: Check sample types only for sampling events

Currently we check sample type for ftrace:function events even if it's not created as a sampling event. That prevents creating ftrace_function event in counting mode.

Make sure we check sample types only for sampling events.

Before:

  $ sudo perf stat -e ftrace:function ls
  ...

Performance counter stats for 'ls':

<not supported> ftrace:function

0.001983662 seconds time elapsed

After:

  $ sudo perf stat -e ftrace:function ls
  ...

Performance counter stats for 'ls':

              44,498      ftrace:function

         0.037534722 seconds time elapsed
Cc Alexander Shishkin <[email protected]>
Cc Arnaldo Carvalho de Melo <[email protected]>
Cc Arnaldo Carvalho de Melo <[email protected]>
Cc Jiri Olsa <[email protected]>
Cc Linus Torvalds <[email protected]>
Cc Peter Zijlstra <[email protected]>
Cc Stephane Eranian <[email protected]>
Cc Thomas Gleixner <[email protected]>
Cc Vince Weaver <[email protected]>
---
 kernel/trace/trace_event_perf.c | 3 +++
 1 file changed, 3 insertions(+)
kernel/trace/trace_event_perf.c +3 -0
index 00df25f..e11108f 100644
@@ -47,6 +47,9 @@ static int perf_trace_event_perm(struct trace_event_call *tp_event,
 		if (perf_paranoid_tracepoint_raw() && !capable(CAP_SYS_ADMIN))
 			return -EPERM;
 
+		if (!is_sampling_event(p_event))
+			return 0;
+
 		/*
 		 * We don't allow user space callchains for  function trace
 		 * event, due to issues with page faults while tracing page
author reviewer
#4 lore raw

PATCH v2 2/5 ftrace perf: Move exclude_kernel tracepoint check to init event

In reply to Jiri Olsa

We suppress events with attr::exclude_kernel set when the event is generated, so following capture will give no warning but won't produce any data:

  $ sudo perf record -e sched:sched_switch:u ls
  $ sudo /perf script | wc -l
  0

Checking the attr::exclude_(kernel|user) at the event init time and failing right away for tracepoints from uprobes/kprobes and native ones:

  $ sudo perf record -e sched:sched_switch:u ls
  Error:
  The sys_perf_event_open() syscall returned with 22 (Invalid argument) for event (sched:sched_switch).
  /bin/dmesg may provide additional information.
  No CONFIG_PERF_EVENTS=y kernel support configured?
  $ sudo perf record -e probe:sys_read:u ls
  Error:
  The sys_perf_event_open() syscall returned with 22 (Invalid argument) for event (probe:sys_read).
  /bin/dmesg may provide additional information.
  No CONFIG_PERF_EVENTS=y kernel support configured?
  $ ./perf record -e probe_ex:main:k ./ex
  Error:
  The sys_perf_event_open() syscall returned with 22 (Invalid argument) for event (probe_ex:main).
  /bin/dmesg may provide additional information.
  No CONFIG_PERF_EVENTS=y kernel support configured?
---
 kernel/events/core.c            |  5 -----
 kernel/trace/trace_event_perf.c | 25 +++++++++++++++++++++++++
 2 files changed, 25 insertions(+), 5 deletions(-)
kernel/events/core.c +0 -5
index c15fd097af93..ca68fdcf47ce 100644
@@ -6898,11 +6898,6 @@ static int perf_tp_event_match(struct perf_event *event,
 {
 	if (event->hw.state & PERF_HES_STOPPED)
 		return 0;
-	/*
-	 * All tracepoints are from kernel-space.
-	 */
-	if (event->attr.exclude_kernel)
-		return 0;
 
 	if (!perf_tp_filter_match(event, data))
 		return 0;
kernel/trace/trace_event_perf.c +25 -0
index e11108f1d197..6e6d4052f398 100644
@@ -185,11 +185,36 @@ static void perf_trace_event_close(struct perf_event *p_event)
 	tp_event->class->reg(tp_event, TRACE_REG_PERF_CLOSE, p_event);
 }
 
+static int perf_trace_event_attr(struct trace_event_call *tp_event,
+				 struct perf_event *event)
+{
+	/*
+	 * All tracepoints and kprobes are from kernel-space.
+	 */
+	if (((tp_event->flags & TRACE_EVENT_FL_TRACEPOINT) ||
+	     (tp_event->flags & TRACE_EVENT_FL_KPROBE)) &&
+	     event->attr.exclude_kernel)
+		return -EINVAL;
+
+	/*
+	 * All uprobes are from user-space.
+	 */
+	if ((tp_event->flags & TRACE_EVENT_FL_UPROBE) &&
+	    event->attr.exclude_user)
+		return -EINVAL;
+
+	return 0;
+}
+
 static int perf_trace_event_init(struct trace_event_call *tp_event,
 				 struct perf_event *p_event)
 {
 	int ret;
 
+	ret = perf_trace_event_attr(tp_event, p_event);
+	if (ret)
+		return ret;
+
 	ret = perf_trace_event_perm(tp_event, p_event);
 	if (ret)
 		return ret;
Signature · 1 line
2.4.3
maintainer Acked-by on v2 2/5
#5 lore raw

Re: [PATCH 2/5] ftrace perf: Move exclude_kernel tracepoint check to init event

Quoted 24 lines · On Wed, 16 Mar 2016 15:34:30 +0100 Jiri Olsa <[email protected]> wrote:

We suppress events with attr::exclude_kernel set when the event is generated, so following capture will give no warning but won't produce any data:

  $ sudo perf record -e sched:sched_switch:u ls
  $ sudo /perf script | wc -l
  0

Checking the attr::exclude_(kernel|user) at the event init time and failing right away for tracepoints from uprobes/kprobes and native ones:

  $ sudo perf record -e sched:sched_switch:u ls
  Error:
  The sys_perf_event_open() syscall returned with 22 (Invalid argument) for event (sched:sched_switch).
  /bin/dmesg may provide additional information.
  No CONFIG_PERF_EVENTS=y kernel support configured?
  $ sudo perf record -e probe:sys_read:u ls
  Error:
  The sys_perf_event_open() syscall returned with 22 (Invalid argument) for event (probe:sys_read).
  /bin/dmesg may provide additional information.
  No CONFIG_PERF_EVENTS=y kernel support configured?
Quoted 8 lines
  $ ./perf record -e probe_ex:main:k ./ex
  Error:
  The sys_perf_event_open() syscall returned with 22 (Invalid argument) for event (probe_ex:main).
  /bin/dmesg may provide additional information.
  No CONFIG_PERF_EVENTS=y kernel support configured?

-- Steve

maintainer
#6 lore raw

Re: [PATCH 2/5] ftrace perf: Move exclude_kernel tracepoint check to init event

In reply to Jiri Olsa
Quoted 24 lines · Jiri Olsa

We suppress events with attr::exclude_kernel set when the event is generated, so following capture will give no warning but won't produce any data:

  $ sudo perf record -e sched:sched_switch:u ls
  $ sudo /perf script | wc -l
  0

Checking the attr::exclude_(kernel|user) at the event init time and failing right away for tracepoints from uprobes/kprobes and native ones:

  $ sudo perf record -e sched:sched_switch:u ls
  Error:
  The sys_perf_event_open() syscall returned with 22 (Invalid argument) for event (sched:sched_switch).
  /bin/dmesg may provide additional information.
  No CONFIG_PERF_EVENTS=y kernel support configured?
  $ sudo perf record -e probe:sys_read:u ls
  Error:
  The sys_perf_event_open() syscall returned with 22 (Invalid argument) for event (probe:sys_read).
  /bin/dmesg may provide additional information.
  No CONFIG_PERF_EVENTS=y kernel support configured?
Quoted 5 lines
  $ ./perf record -e probe_ex:main:k ./ex
  Error:
  The sys_perf_event_open() syscall returned with 22 (Invalid argument) for event (probe_ex:main).
  /bin/dmesg may provide additional information.
  No CONFIG_PERF_EVENTS=y kernel support configured?

Not sure about this one. The previous behaviour suggests exclude_{user,kernel} is implemented, while the new behaviour says these flags are not implemented, which is a functional regression.

That is, if all events are from kernel space, and we exclude all kernel events, 0 is the right answer not an error.

Sure, with uprobes the situation is currently broken, but this isn't a fix.

#7 lore raw

Re: [PATCH 2/5] ftrace perf: Move exclude_kernel tracepoint check to init event

Quoted 31 lines · Peter Zijlstra
Quoted 24 lines · Jiri Olsa

We suppress events with attr::exclude_kernel set when the event is generated, so following capture will give no warning but won't produce any data:

  $ sudo perf record -e sched:sched_switch:u ls
  $ sudo /perf script | wc -l
  0

Checking the attr::exclude_(kernel|user) at the event init time and failing right away for tracepoints from uprobes/kprobes and native ones:

  $ sudo perf record -e sched:sched_switch:u ls
  Error:
  The sys_perf_event_open() syscall returned with 22 (Invalid argument) for event (sched:sched_switch).
  /bin/dmesg may provide additional information.
  No CONFIG_PERF_EVENTS=y kernel support configured?
  $ sudo perf record -e probe:sys_read:u ls
  Error:
  The sys_perf_event_open() syscall returned with 22 (Invalid argument) for event (probe:sys_read).
  /bin/dmesg may provide additional information.
  No CONFIG_PERF_EVENTS=y kernel support configured?
Quoted 5 lines
  $ ./perf record -e probe_ex:main:k ./ex
  Error:
  The sys_perf_event_open() syscall returned with 22 (Invalid argument) for event (probe_ex:main).
  /bin/dmesg may provide additional information.
  No CONFIG_PERF_EVENTS=y kernel support configured?
Quoted 3 lines

Not sure about this one. The previous behaviour suggests exclude_{user,kernel} is implemented, while the new behaviour says these flags are not implemented, which is a functional regression.

well I would not expect 'sched:sched_switch:u' to work (be implemented)

and I thought it's better to trigger an error than silently 'produce' no data

Quoted 6 lines

That is, if all events are from kernel space, and we exclude all kernel events, 0 is the right answer not an error.

Sure, with uprobes the situation is currently broken, but this isn't a fix.

maintainer
#8 lore raw

Re: [PATCH 2/5] ftrace perf: Move exclude_kernel tracepoint check to init event

Quoted 20 lines · Jiri Olsa
Quoted 18 lines · Peter Zijlstra
Quoted 17 lines · Jiri Olsa

We suppress events with attr::exclude_kernel set when the event is generated, so following capture will give no warning but won't produce any data:

  $ sudo perf record -e sched:sched_switch:u ls
  $ sudo /perf script | wc -l
  0

Checking the attr::exclude_(kernel|user) at the event init time and failing right away for tracepoints from uprobes/kprobes and native ones:

  $ sudo perf record -e sched:sched_switch:u ls
  Error:
  The sys_perf_event_open() syscall returned with 22 (Invalid argument) for event (sched:sched_switch).
  /bin/dmesg may provide additional information.
  No CONFIG_PERF_EVENTS=y kernel support configured?
Quoted 7 lines
Quoted 3 lines

Not sure about this one. The previous behaviour suggests exclude_{user,kernel} is implemented, while the new behaviour says these flags are not implemented, which is a functional regression.

well I would not expect 'sched:sched_switch:u' to work (be implemented)

and I thought it's better to trigger an error than silently 'produce' no data

We're not in the business of protecting people from themselves are we? And if you want to help them, do so in userspace.

And its not like the [uk] flags are hard to implement here.

#9 lore raw

Re: [PATCH 2/5] ftrace perf: Move exclude_kernel tracepoint check to init event

Quoted 25 lines · Peter Zijlstra
Quoted 24 lines · Jiri Olsa
Quoted 22 lines · Peter Zijlstra
Quoted 12 lines · Jiri Olsa

We suppress events with attr::exclude_kernel set when the event is generated, so following capture will give no warning but won't produce any data:

  $ sudo perf record -e sched:sched_switch:u ls
  $ sudo /perf script | wc -l
  0

Checking the attr::exclude_(kernel|user) at the event init time and failing right away for tracepoints from uprobes/kprobes and native ones:

Quoted 5 lines
  $ sudo perf record -e sched:sched_switch:u ls
  Error:
  The sys_perf_event_open() syscall returned with 22 (Invalid argument) for event (sched:sched_switch).
  /bin/dmesg may provide additional information.
  No CONFIG_PERF_EVENTS=y kernel support configured?

Not sure about this one. The previous behaviour suggests exclude_{user,kernel} is implemented, while the new behaviour says these flags are not implemented, which is a functional regression.

Quoted 6 lines
Quoted 3 lines

well I would not expect 'sched:sched_switch:u' to work (be implemented)

and I thought it's better to trigger an error than silently 'produce' no data

We're not in the business of protecting people from themselves are we? And if you want to help them, do so in userspace.

yep, I planned to make user space patch
to make that error more user friendly

Quoted 2 lines

And its not like the [uk] flags are hard to implement here.

sched:sched_switch:u ?

jirka

maintainer
#10 lore raw

Re: [PATCH 2/5] ftrace perf: Move exclude_kernel tracepoint check to init event

Quoted 7 lines · Jiri Olsa
Quoted 5 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
  $ sudo perf record -e sched:sched_switch:u ls
  $ sudo /perf script | wc -l
  0

And its not like the [uk] flags are hard to implement here.

sched:sched_switch:u ?

As per the above, its implemented and correct. The answer is 0.

The only problem is that currently it assumes all tracepoint events are from the kernel, and that is wrong for uprobes.

But returning an error is not right, its a valid configuration, a daft one, sure, but not invalid.

Similarly uprobe:foo:k is daft, but broken.

#11 lore raw

Re: [PATCH 2/5] ftrace perf: Move exclude_kernel tracepoint check to init event

Quoted 11 lines · Peter Zijlstra
Quoted 7 lines · Jiri Olsa
Quoted 5 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
  $ sudo perf record -e sched:sched_switch:u ls
  $ sudo /perf script | wc -l
  0

And its not like the [uk] flags are hard to implement here.

sched:sched_switch:u ?

As per the above, its implemented and correct. The answer is 0.

Quoted 7 lines

The only problem is that currently it assumes all tracepoint events are from the kernel, and that is wrong for uprobes.

But returning an error is not right, its a valid configuration, a daft one, sure, but not invalid.

Similarly uprobe:foo:k is daft, but broken.

ok, will send fix for uprobes then

thanks,
jirka

author reviewer
#12 lore raw

PATCH v2 3/5 ftrace perf: Use ftrace_ops::private to store event pointer

In reply to Jiri Olsa

Having following commands running concurrently:

  # perf record -e ftrace:function -a -o krava.data sleep 10
  # perf record -e ftrace:function --filter 'ip == SyS_read' ls

will end up in the latter one to fail on the filter rules and store all functions (in perf.data) as instructed by the first perf record instead of just SyS_read records.

The reason for this is, that tracepoint code by default triggers all events that registered for the tracepoint.

While ftrace:function is special because ftrace_ops itself carries a filter and only the event that owns ftrace_ops is eligible to be triggered.

Fixing this by using ftrace_ops::private value to keep the perf_event pointer. This way we don't need to search for triggered event (as tracepoint handler does) and directly store sample.

---
 include/linux/perf_event.h      |  3 +++
 kernel/events/core.c            | 22 ++++++++++++++++++++++
 kernel/trace/trace_event_perf.c | 10 +++-------
 3 files changed, 28 insertions(+), 7 deletions(-)
include/linux/perf_event.h +3 -0
index a9d8cab18b00..a330dc06d90d 100644
@@ -1008,6 +1008,9 @@ extern void perf_tp_event(u64 addr, u64 count, void *record,
 			  int entry_size, struct pt_regs *regs,
 			  struct hlist_head *head, int rctx,
 			  struct task_struct *task);
+void perf_function_event(struct perf_event *event,
+			 void *record, int entry_size,
+			 struct pt_regs *regs);
 extern void perf_bp_event(struct perf_event *event, void *data);
 
 #ifndef perf_misc_flags
kernel/events/core.c +22 -0
index ca68fdcf47ce..18da90859c17 100644
@@ -7063,6 +7063,28 @@ static void perf_event_free_bpf_prog(struct perf_event *event)
 	}
 }
 
+#ifdef CONFIG_FUNCTION_TRACER
+void perf_function_event(struct perf_event *event,
+			 void *record, int entry_size,
+			 struct pt_regs *regs)
+
+{
+	struct perf_sample_data data;
+	struct perf_raw_record raw = {
+		.size = entry_size,
+		.data = record,
+	};
+
+	if (event->hw.state & PERF_HES_STOPPED)
+		return;
+
+	perf_sample_data_init(&data, 0, 0);
+	data.raw = &raw;
+
+	perf_swevent_event(event, 1, &data, regs);
+}
+#endif /* CONFIG_FUNCTION_TRACER */
+
 #else
 
 static inline void perf_tp_register(void)
kernel/trace/trace_event_perf.c +3 -7
index 6e6d4052f398..37de72405a64 100644
@@ -331,14 +331,9 @@ perf_ftrace_function_call(unsigned long ip, unsigned long parent_ip,
 			  struct ftrace_ops *ops, struct pt_regs *pt_regs)
 {
 	struct ftrace_entry *entry;
-	struct hlist_head *head;
 	struct pt_regs regs;
 	int rctx;
 
-	head = this_cpu_ptr(event_function.perf_events);
-	if (hlist_empty(head))
-		return;
-
 #define ENTRY_SIZE (ALIGN(sizeof(struct ftrace_entry) + sizeof(u32), \
 		    sizeof(u64)) - sizeof(u32))
 
@@ -352,8 +347,8 @@ perf_ftrace_function_call(unsigned long ip, unsigned long parent_ip,
 
 	entry->ip = ip;
 	entry->parent_ip = parent_ip;
-	perf_trace_buf_submit(entry, ENTRY_SIZE, rctx, 0,
-			      1, &regs, head, NULL);
+	perf_function_event(ops->private, entry, ENTRY_SIZE, &regs);
+	perf_swevent_put_recursion_context(rctx);
 
 #undef ENTRY_SIZE
 }
@@ -362,6 +357,7 @@ static int perf_ftrace_function_register(struct perf_event *event)
 {
 	struct ftrace_ops *ops = &event->ftrace_ops;
 
+	ops->private = event;
 	ops->flags |= FTRACE_OPS_FL_PER_CPU | FTRACE_OPS_FL_RCU;
 	ops->func = perf_ftrace_function_call;
 	return register_ftrace_function(ops);
Signature · 1 line
2.4.3
maintainer Acked-by on v2 3/5
#13 lore raw

Re: [PATCH 3/5] ftrace perf: Use ftrace_ops::private to store event pointer

Quoted 16 lines · On Wed, 16 Mar 2016 15:34:31 +0100 Jiri Olsa <[email protected]> wrote:

Having following commands running concurrently:

  # perf record -e ftrace:function -a -o krava.data sleep 10
  # perf record -e ftrace:function --filter 'ip == SyS_read' ls

will end up in the latter one to fail on the filter rules and store all functions (in perf.data) as instructed by the first perf record instead of just SyS_read records.

The reason for this is, that tracepoint code by default triggers all events that registered for the tracepoint.

While ftrace:function is special because ftrace_ops itself carries a filter and only the event that owns ftrace_ops is eligible to be triggered.

Quoted 8 lines

Fixing this by using ftrace_ops::private value to keep the perf_event pointer. This way we don't need to search for triggered event (as tracepoint handler does) and directly store sample.

-- Steve

maintainer
#14 lore raw

Re: [PATCH 3/5] ftrace perf: Use ftrace_ops::private to store event pointer

In reply to Jiri Olsa
Quoted 4 lines · Jiri Olsa

Fixing this by using ftrace_ops::private value to keep the perf_event pointer. This way we don't need to search for triggered event (as tracepoint handler does) and directly store sample.

container_of(ops, struct perf_event, ftrace_ops) would also have worked, right?

#15 lore raw

Re: [PATCH 3/5] ftrace perf: Use ftrace_ops::private to store event pointer

Quoted 8 lines · Peter Zijlstra
Quoted 4 lines · Jiri Olsa

Fixing this by using ftrace_ops::private value to keep the perf_event pointer. This way we don't need to search for triggered event (as tracepoint handler does) and directly store sample.

container_of(ops, struct perf_event, ftrace_ops) would also have worked, right?

yes, I think that would work too

jirka

maintainer
#16 lore raw

Re: [PATCH 3/5] ftrace perf: Use ftrace_ops::private to store event pointer

Quoted 11 lines · Jiri Olsa
Quoted 8 lines · Peter Zijlstra
Quoted 4 lines · Jiri Olsa

Fixing this by using ftrace_ops::private value to keep the perf_event pointer. This way we don't need to search for triggered event (as tracepoint handler does) and directly store sample.

container_of(ops, struct perf_event, ftrace_ops) would also have worked, right?

yes, I think that would work too

OK, no need to change, I was just seeing if I actually understood the code.

author reviewer
#17 lore raw

PATCH v2 4/5 ftrace: Make ftrace_hash_rec_enable return update bool

In reply to Jiri Olsa

Change __ftrace_hash_rec_update to return true in case we need to update dynamic ftrace call records. It return false in case no update is needed.

---
 kernel/trace/ftrace.c | 27 +++++++++++++++++----------
 1 file changed, 17 insertions(+), 10 deletions(-)
kernel/trace/ftrace.c +17 -10
index eca592f977b2..3a9a12215b50 100644
@@ -1610,7 +1610,7 @@ static bool test_rec_ops_needs_regs(struct dyn_ftrace *rec)
 	return  keep_regs;
 }
 
-static void __ftrace_hash_rec_update(struct ftrace_ops *ops,
+static bool __ftrace_hash_rec_update(struct ftrace_ops *ops,
 				     int filter_hash,
 				     bool inc)
 {
@@ -1618,12 +1618,13 @@ static void __ftrace_hash_rec_update(struct ftrace_ops *ops,
 	struct ftrace_hash *other_hash;
 	struct ftrace_page *pg;
 	struct dyn_ftrace *rec;
+	bool update = false;
 	int count = 0;
 	int all = 0;
 
 	/* Only update if the ops has been registered */
 	if (!(ops->flags & FTRACE_OPS_FL_ENABLED))
-		return;
+		return false;
 
 	/*
 	 * In the filter_hash case:
@@ -1650,7 +1651,7 @@ static void __ftrace_hash_rec_update(struct ftrace_ops *ops,
 		 * then there's nothing to do.
 		 */
 		if (ftrace_hash_empty(hash))
-			return;
+			return false;
 	}
 
 	do_for_each_ftrace_rec(pg, rec) {
@@ -1694,7 +1695,7 @@ static void __ftrace_hash_rec_update(struct ftrace_ops *ops,
 		if (inc) {
 			rec->flags++;
 			if (FTRACE_WARN_ON(ftrace_rec_count(rec) == FTRACE_REF_MAX))
-				return;
+				return false;
 
 			/*
 			 * If there's only a single callback registered to a
@@ -1720,7 +1721,7 @@ static void __ftrace_hash_rec_update(struct ftrace_ops *ops,
 				rec->flags |= FTRACE_FL_REGS;
 		} else {
 			if (FTRACE_WARN_ON(ftrace_rec_count(rec) == 0))
-				return;
+				return false;
 			rec->flags--;
 
 			/*
@@ -1753,22 +1754,28 @@ static void __ftrace_hash_rec_update(struct ftrace_ops *ops,
 			 */
 		}
 		count++;
+
+		/* Must match FTRACE_UPDATE_CALLS in ftrace_modify_all_code() */
+		update |= ftrace_test_record(rec, 1) != FTRACE_UPDATE_IGNORE;
+
 		/* Shortcut, if we handled all records, we are done. */
 		if (!all && count == hash->count)
-			return;
+			return update;
 	} while_for_each_ftrace_rec();
+
+	return update;
 }
 
-static void ftrace_hash_rec_disable(struct ftrace_ops *ops,
+static bool ftrace_hash_rec_disable(struct ftrace_ops *ops,
 				    int filter_hash)
 {
-	__ftrace_hash_rec_update(ops, filter_hash, 0);
+	return __ftrace_hash_rec_update(ops, filter_hash, 0);
 }
 
-static void ftrace_hash_rec_enable(struct ftrace_ops *ops,
+static bool ftrace_hash_rec_enable(struct ftrace_ops *ops,
 				   int filter_hash)
 {
-	__ftrace_hash_rec_update(ops, filter_hash, 1);
+	return __ftrace_hash_rec_update(ops, filter_hash, 1);
 }
 
 static void ftrace_hash_rec_update_modify(struct ftrace_ops *ops,
Signature · 1 line
2.4.3
maintainer Acked-by on v2 4/5
#18 lore raw

Re: [PATCH 4/5] ftrace: Make ftrace_hash_rec_enable return update bool

Hi Jiri,

Quoted 5 lines · Jiri Olsa

Change __ftrace_hash_rec_update to return true in case we need to update dynamic ftrace call records. It return false in case no update is needed.

Thanks,
Namhyung

Quoted 96 lines
---
 kernel/trace/ftrace.c | 27 +++++++++++++++++----------
 1 file changed, 17 insertions(+), 10 deletions(-)
kernel/trace/ftrace.c +17 -10
index eca592f977b2..3a9a12215b50 100644
@@ -1610,7 +1610,7 @@ static bool test_rec_ops_needs_regs(struct dyn_ftrace *rec)
 	return  keep_regs;
 }
 
-static void __ftrace_hash_rec_update(struct ftrace_ops *ops,
+static bool __ftrace_hash_rec_update(struct ftrace_ops *ops,
 				     int filter_hash,
 				     bool inc)
 {
@@ -1618,12 +1618,13 @@ static void __ftrace_hash_rec_update(struct ftrace_ops *ops,
 	struct ftrace_hash *other_hash;
 	struct ftrace_page *pg;
 	struct dyn_ftrace *rec;
+	bool update = false;
 	int count = 0;
 	int all = 0;
 
 	/* Only update if the ops has been registered */
 	if (!(ops->flags & FTRACE_OPS_FL_ENABLED))
-		return;
+		return false;
 
 	/*
 	 * In the filter_hash case:
@@ -1650,7 +1651,7 @@ static void __ftrace_hash_rec_update(struct ftrace_ops *ops,
 		 * then there's nothing to do.
 		 */
 		if (ftrace_hash_empty(hash))
-			return;
+			return false;
 	}
 
 	do_for_each_ftrace_rec(pg, rec) {
@@ -1694,7 +1695,7 @@ static void __ftrace_hash_rec_update(struct ftrace_ops *ops,
 		if (inc) {
 			rec->flags++;
 			if (FTRACE_WARN_ON(ftrace_rec_count(rec) == FTRACE_REF_MAX))
-				return;
+				return false;
 
 			/*
 			 * If there's only a single callback registered to a
@@ -1720,7 +1721,7 @@ static void __ftrace_hash_rec_update(struct ftrace_ops *ops,
 				rec->flags |= FTRACE_FL_REGS;
 		} else {
 			if (FTRACE_WARN_ON(ftrace_rec_count(rec) == 0))
-				return;
+				return false;
 			rec->flags--;
 
 			/*
@@ -1753,22 +1754,28 @@ static void __ftrace_hash_rec_update(struct ftrace_ops *ops,
 			 */
 		}
 		count++;
+
+		/* Must match FTRACE_UPDATE_CALLS in ftrace_modify_all_code() */
+		update |= ftrace_test_record(rec, 1) != FTRACE_UPDATE_IGNORE;
+
 		/* Shortcut, if we handled all records, we are done. */
 		if (!all && count == hash->count)
-			return;
+			return update;
 	} while_for_each_ftrace_rec();
+
+	return update;
 }
 
-static void ftrace_hash_rec_disable(struct ftrace_ops *ops,
+static bool ftrace_hash_rec_disable(struct ftrace_ops *ops,
 				    int filter_hash)
 {
-	__ftrace_hash_rec_update(ops, filter_hash, 0);
+	return __ftrace_hash_rec_update(ops, filter_hash, 0);
 }
 
-static void ftrace_hash_rec_enable(struct ftrace_ops *ops,
+static bool ftrace_hash_rec_enable(struct ftrace_ops *ops,
 				   int filter_hash)
 {
-	__ftrace_hash_rec_update(ops, filter_hash, 1);
+	return __ftrace_hash_rec_update(ops, filter_hash, 1);
 }
 
 static void ftrace_hash_rec_update_modify(struct ftrace_ops *ops,
Signature · 1 line
2.4.3
author reviewer
#19 lore raw

PATCH v2 5/5 ftrace: Update dynamic ftrace calls only if necessary

In reply to Jiri Olsa

Currently dynamic ftrace calls are updated any time the ftrace_ops is un/registered. If we do this update only when it's needed, we save lot of time for perf system wide ftrace function sampling/counting.

The reason is that for system wide sampling/counting, perf creates event for each cpu in the system.

Each event then registers separate copy of ftrace_ops, which ends up in FTRACE_UPDATE_CALLS updates. On servers with many cpus that means serious stall (240 cpus server):

Counting:

  # time ./perf stat -e ftrace:function -a sleep 1

Performance counter stats for 'system wide':

              370,663      ftrace:function

          1.401427505 seconds time elapsed
  real    3m51.743s
  user    0m0.023s
  sys     3m48.569s

Sampling:

  # time ./perf record -e ftrace:function -a sleep 1
  [ perf record: Woken up 0 times to write data ]
  Warning:
  Processed 141200 events and lost 5 chunks!

[ perf record: Captured and wrote 10.703 MB perf.data (135950 samples) ]

  real    2m31.429s
  user    0m0.213s
  sys     2m29.494s

There's no reason to do the FTRACE_UPDATE_CALLS update for each event in perf case, because all the ftrace_ops always share the same filter, so the updated calls are always the same.

It's required that only first ftrace_ops registration does the FTRACE_UPDATE_CALLS update (also sometimes the second if the first one used the trampoline), but the rest can be only cheaply linked into the ftrace_ops list.

Counting:

  # time ./perf stat -e ftrace:function -a sleep 1

Performance counter stats for 'system wide':

             398,571      ftrace:function

         1.377503733 seconds time elapsed
  real    0m2.787s
  user    0m0.005s
  sys     0m1.883s

Sampling:

  # time ./perf record -e ftrace:function -a sleep 1
  [ perf record: Woken up 0 times to write data ]
  Warning:
  Processed 261730 events and lost 9 chunks!

[ perf record: Captured and wrote 19.907 MB perf.data (256293 samples) ]

  real    1m31.948s
  user    0m0.309s
  sys     1m32.051s
---
 kernel/trace/ftrace.c | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)
kernel/trace/ftrace.c +5 -5
index 3a9a12215b50..22ef6fc597a2 100644
@@ -2651,7 +2651,6 @@ static int ftrace_startup(struct ftrace_ops *ops, int command)
 		return ret;
 
 	ftrace_start_up++;
-	command |= FTRACE_UPDATE_CALLS;
 
 	/*
 	 * Note that ftrace probes uses this to start up
@@ -2672,7 +2671,8 @@ static int ftrace_startup(struct ftrace_ops *ops, int command)
 		return ret;
 	}
 
-	ftrace_hash_rec_enable(ops, 1);
+	if (ftrace_hash_rec_enable(ops, 1))
+		command |= FTRACE_UPDATE_CALLS;
 
 	ftrace_startup_enable(command);
 
@@ -2702,11 +2702,11 @@ static int ftrace_shutdown(struct ftrace_ops *ops, int command)
 
 	/* Disabling ipmodify never fails */
 	ftrace_hash_ipmodify_disable(ops);
-	ftrace_hash_rec_disable(ops, 1);
 
-	ops->flags &= ~FTRACE_OPS_FL_ENABLED;
+	if (ftrace_hash_rec_disable(ops, 1))
+		command |= FTRACE_UPDATE_CALLS;
 
-	command |= FTRACE_UPDATE_CALLS;
+	ops->flags &= ~FTRACE_OPS_FL_ENABLED;
 
 	if (saved_ftrace_func != ftrace_trace_function) {
 		saved_ftrace_func = ftrace_trace_function;
Signature · 1 line
2.4.3
maintainer Acked-by on v2 5/5
#20 lore raw

Re: [PATCH 5/5] ftrace: Update dynamic ftrace calls only if necessary

Quoted 67 lines · Jiri Olsa

Currently dynamic ftrace calls are updated any time the ftrace_ops is un/registered. If we do this update only when it's needed, we save lot of time for perf system wide ftrace function sampling/counting.

The reason is that for system wide sampling/counting, perf creates event for each cpu in the system.

Each event then registers separate copy of ftrace_ops, which ends up in FTRACE_UPDATE_CALLS updates. On servers with many cpus that means serious stall (240 cpus server):

Counting:

  # time ./perf stat -e ftrace:function -a sleep 1
   Performance counter stats for 'system wide':

              370,663      ftrace:function

          1.401427505 seconds time elapsed
  real    3m51.743s
  user    0m0.023s
  sys     3m48.569s

Sampling:

  # time ./perf record -e ftrace:function -a sleep 1
  [ perf record: Woken up 0 times to write data ]
  Warning:
  Processed 141200 events and lost 5 chunks!

[ perf record: Captured and wrote 10.703 MB perf.data (135950 samples) ]

  real    2m31.429s
  user    0m0.213s
  sys     2m29.494s

There's no reason to do the FTRACE_UPDATE_CALLS update for each event in perf case, because all the ftrace_ops always share the same filter, so the updated calls are always the same.

It's required that only first ftrace_ops registration does the FTRACE_UPDATE_CALLS update (also sometimes the second if the first one used the trampoline), but the rest can be only cheaply linked into the ftrace_ops list.

Counting:

  # time ./perf stat -e ftrace:function -a sleep 1
   Performance counter stats for 'system wide':

             398,571      ftrace:function

         1.377503733 seconds time elapsed
  real    0m2.787s
  user    0m0.005s
  sys     0m1.883s

Sampling:

  # time ./perf record -e ftrace:function -a sleep 1
  [ perf record: Woken up 0 times to write data ]
  Warning:
  Processed 261730 events and lost 9 chunks!
Quoted 7 lines

[ perf record: Captured and wrote 19.907 MB perf.data (256293 samples) ]

  real    1m31.948s
  user    0m0.309s
  sys     1m32.051s

Thanks,
Namhyung

Quoted 44 lines
---
 kernel/trace/ftrace.c | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)
kernel/trace/ftrace.c +5 -5
index 3a9a12215b50..22ef6fc597a2 100644
@@ -2651,7 +2651,6 @@ static int ftrace_startup(struct ftrace_ops *ops, int command)
 		return ret;
 
 	ftrace_start_up++;
-	command |= FTRACE_UPDATE_CALLS;
 
 	/*
 	 * Note that ftrace probes uses this to start up
@@ -2672,7 +2671,8 @@ static int ftrace_startup(struct ftrace_ops *ops, int command)
 		return ret;
 	}
 
-	ftrace_hash_rec_enable(ops, 1);
+	if (ftrace_hash_rec_enable(ops, 1))
+		command |= FTRACE_UPDATE_CALLS;
 
 	ftrace_startup_enable(command);
 
@@ -2702,11 +2702,11 @@ static int ftrace_shutdown(struct ftrace_ops *ops, int command)
 
 	/* Disabling ipmodify never fails */
 	ftrace_hash_ipmodify_disable(ops);
-	ftrace_hash_rec_disable(ops, 1);
 
-	ops->flags &= ~FTRACE_OPS_FL_ENABLED;
+	if (ftrace_hash_rec_disable(ops, 1))
+		command |= FTRACE_UPDATE_CALLS;
 
-	command |= FTRACE_UPDATE_CALLS;
+	ops->flags &= ~FTRACE_OPS_FL_ENABLED;
 
 	if (saved_ftrace_func != ftrace_trace_function) {
 		saved_ftrace_func = ftrace_trace_function;
Signature · 1 line
2.4.3
maintainer
#21 lore raw

Re: [PATCH 5/5] ftrace: Update dynamic ftrace calls only if necessary

In reply to Jiri Olsa

Patches 4 and 5 seem agnostic to patches 1-3. I'll pull 4,5 into my tree and 1-3 can go though tip. I'd like to run theses through my tests.

OK?

-- Steve

Quoted 75 lines · On Wed, 16 Mar 2016 15:34:33 +0100 Jiri Olsa <[email protected]> wrote:

Currently dynamic ftrace calls are updated any time the ftrace_ops is un/registered. If we do this update only when it's needed, we save lot of time for perf system wide ftrace function sampling/counting.

The reason is that for system wide sampling/counting, perf creates event for each cpu in the system.

Each event then registers separate copy of ftrace_ops, which ends up in FTRACE_UPDATE_CALLS updates. On servers with many cpus that means serious stall (240 cpus server):

Counting:

  # time ./perf stat -e ftrace:function -a sleep 1
   Performance counter stats for 'system wide':

              370,663      ftrace:function

          1.401427505 seconds time elapsed
  real    3m51.743s
  user    0m0.023s
  sys     3m48.569s

Sampling:

  # time ./perf record -e ftrace:function -a sleep 1
  [ perf record: Woken up 0 times to write data ]
  Warning:
  Processed 141200 events and lost 5 chunks!

[ perf record: Captured and wrote 10.703 MB perf.data (135950 samples) ]

  real    2m31.429s
  user    0m0.213s
  sys     2m29.494s

There's no reason to do the FTRACE_UPDATE_CALLS update for each event in perf case, because all the ftrace_ops always share the same filter, so the updated calls are always the same.

It's required that only first ftrace_ops registration does the FTRACE_UPDATE_CALLS update (also sometimes the second if the first one used the trampoline), but the rest can be only cheaply linked into the ftrace_ops list.

Counting:

  # time ./perf stat -e ftrace:function -a sleep 1
   Performance counter stats for 'system wide':

             398,571      ftrace:function

         1.377503733 seconds time elapsed
  real    0m2.787s
  user    0m0.005s
  sys     0m1.883s

Sampling:

  # time ./perf record -e ftrace:function -a sleep 1
  [ perf record: Woken up 0 times to write data ]
  Warning:
  Processed 261730 events and lost 9 chunks!

[ perf record: Captured and wrote 19.907 MB perf.data (256293 samples) ]

  real    1m31.948s
  user    0m0.309s
  sys     1m32.051s

---