* [PATCH V9 1/6] tracing: add a possibility of exporting function trace to other places instead of ring buffer only
2016-11-21 7:57 [PATCH V9 0/6] Integration of function trace with System Trace IP blocks Chunyan Zhang
@ 2016-11-21 7:57 ` Chunyan Zhang
2016-11-22 22:39 ` Steven Rostedt
2016-11-21 7:57 ` [PATCH V9 2/6] stm class: ftrace: Add ftrace-export-over-stm driver Chunyan Zhang
` (4 subsequent siblings)
5 siblings, 1 reply; 10+ messages in thread
From: Chunyan Zhang @ 2016-11-21 7:57 UTC (permalink / raw)
To: rostedt, mathieu.poirier, alexander.shishkin, mingo
Cc: mike.leach, tor, philippe.langlais, nicolas.guion, felipe.balbi,
zhang.lyra, linux-kernel, linux-arm-kernel
Currently Function traces can be only exported to ring buffer, this
patch added trace_export concept which can process traces and export
them to a registered destination as an addition to the current only
one output of Ftrace - i.e. ring buffer.
In this way, if we want Function traces to be sent to other destination
rather than ring buffer only, we just need to register a new trace_export
and implement its own .write() function for writing traces to storage.
With this patch, only Function trace (trace type is TRACE_FN)
is supported.
Signed-off-by: Chunyan Zhang <zhang.chunyan@linaro.org>
---
include/linux/trace.h | 28 +++++++++++
kernel/trace/trace.c | 129 +++++++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 156 insertions(+), 1 deletion(-)
create mode 100644 include/linux/trace.h
diff --git a/include/linux/trace.h b/include/linux/trace.h
new file mode 100644
index 0000000..9330a58
--- /dev/null
+++ b/include/linux/trace.h
@@ -0,0 +1,28 @@
+#ifndef _LINUX_TRACE_H
+#define _LINUX_TRACE_H
+
+#ifdef CONFIG_TRACING
+/*
+ * The trace export - an export of Ftrace output. The trace_export
+ * can process traces and export them to a registered destination as
+ * an addition to the current only output of Ftrace - i.e. ring buffer.
+ *
+ * If you want traces to be sent to some other place rather than ring
+ * buffer only, just need to register a new trace_export and implement
+ * its own .write() function for writing traces to the storage.
+ *
+ * next - pointer to the next trace_export
+ * write - copy traces which have been delt with ->commit() to
+ * the destination
+ */
+struct trace_export {
+ struct trace_export __rcu *next;
+ void (*write)(const void *, unsigned int);
+};
+
+int register_ftrace_export(struct trace_export *export);
+int unregister_ftrace_export(struct trace_export *export);
+
+#endif /* CONFIG_TRACING */
+
+#endif /* _LINUX_TRACE_H */
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index 8696ce6..038291d 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -40,6 +40,7 @@
#include <linux/poll.h>
#include <linux/nmi.h>
#include <linux/fs.h>
+#include <linux/trace.h>
#include <linux/sched/rt.h>
#include "trace.h"
@@ -2128,6 +2129,129 @@ void trace_buffer_unlock_commit_regs(struct trace_array *tr,
ftrace_trace_userstack(buffer, flags, pc);
}
+static void
+trace_process_export(struct trace_export *export,
+ struct ring_buffer_event *event)
+{
+ struct trace_entry *entry;
+ unsigned int size = 0;
+
+ entry = ring_buffer_event_data(event);
+ size = ring_buffer_event_length(event);
+ export->write(entry, size);
+}
+
+static DEFINE_MUTEX(ftrace_export_lock);
+
+static struct trace_export __rcu *ftrace_exports_list __read_mostly;
+
+static DEFINE_STATIC_KEY_FALSE(ftrace_exports_enabled);
+
+static inline void ftrace_exports_enable(void)
+{
+ static_branch_enable(&ftrace_exports_enabled);
+}
+
+static inline void ftrace_exports_disable(void)
+{
+ static_branch_disable(&ftrace_exports_enabled);
+}
+
+void ftrace_exports(struct ring_buffer_event *event)
+{
+ struct trace_export *export;
+
+ preempt_disable_notrace();
+
+ export = rcu_dereference_raw_notrace(ftrace_exports_list);
+ while (export) {
+ trace_process_export(export, event);
+ export = rcu_dereference_raw_notrace(export->next);
+ }
+
+ preempt_enable_notrace();
+}
+
+static inline void
+add_trace_export(struct trace_export **list, struct trace_export *export)
+{
+ rcu_assign_pointer(export->next, *list);
+ /*
+ * We are entering export into the list but another
+ * CPU might be walking that list. We need to make sure
+ * the export->next pointer is valid before another CPU sees
+ * the export pointer included into the list.
+ */
+ rcu_assign_pointer(*list, export);
+}
+
+static inline int
+rm_trace_export(struct trace_export **list, struct trace_export *export)
+{
+ struct trace_export **p;
+
+ for (p = list; *p != NULL; p = &(*p)->next)
+ if (*p == export)
+ break;
+
+ if (*p != export)
+ return -1;
+
+ rcu_assign_pointer(*p, (*p)->next);
+
+ return 0;
+}
+
+static inline void
+add_ftrace_export(struct trace_export **list, struct trace_export *export)
+{
+ if (*list == NULL)
+ ftrace_exports_enable();
+
+ add_trace_export(list, export);
+}
+
+static inline int
+rm_ftrace_export(struct trace_export **list, struct trace_export *export)
+{
+ int ret;
+
+ ret = rm_trace_export(list, export);
+ if (*list == NULL)
+ ftrace_exports_disable();
+
+ return ret;
+}
+
+int register_ftrace_export(struct trace_export *export)
+{
+ if (WARN_ON_ONCE(!export->write))
+ return -1;
+
+ mutex_lock(&ftrace_export_lock);
+
+ add_ftrace_export(&ftrace_exports_list, export);
+
+ mutex_unlock(&ftrace_export_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(register_ftrace_export);
+
+int unregister_ftrace_export(struct trace_export *export)
+{
+ int ret;
+
+ mutex_lock(&ftrace_export_lock);
+
+ ret = rm_ftrace_export(&ftrace_exports_list, export);
+
+ mutex_unlock(&ftrace_export_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(unregister_ftrace_export);
+
void
trace_function(struct trace_array *tr,
unsigned long ip, unsigned long parent_ip, unsigned long flags,
@@ -2146,8 +2270,11 @@ trace_function(struct trace_array *tr,
entry->ip = ip;
entry->parent_ip = parent_ip;
- if (!call_filter_check_discard(call, entry, buffer, event))
+ if (!call_filter_check_discard(call, entry, buffer, event)) {
+ if (static_branch_unlikely(&ftrace_exports_enabled))
+ ftrace_exports(event);
__buffer_unlock_commit(buffer, event);
+ }
}
#ifdef CONFIG_STACKTRACE
--
2.7.4
^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH V9 1/6] tracing: add a possibility of exporting function trace to other places instead of ring buffer only
2016-11-21 7:57 ` [PATCH V9 1/6] tracing: add a possibility of exporting function trace to other places instead of ring buffer only Chunyan Zhang
@ 2016-11-22 22:39 ` Steven Rostedt
2016-11-23 2:27 ` Chunyan Zhang
0 siblings, 1 reply; 10+ messages in thread
From: Steven Rostedt @ 2016-11-22 22:39 UTC (permalink / raw)
To: Chunyan Zhang
Cc: mathieu.poirier, alexander.shishkin, mingo, mike.leach, tor,
philippe.langlais, nicolas.guion, felipe.balbi, zhang.lyra,
linux-kernel, linux-arm-kernel
On Mon, 21 Nov 2016 15:57:18 +0800
Chunyan Zhang <zhang.chunyan@linaro.org> wrote:
> Currently Function traces can be only exported to ring buffer, this
> patch added trace_export concept which can process traces and export
> them to a registered destination as an addition to the current only
> one output of Ftrace - i.e. ring buffer.
>
> In this way, if we want Function traces to be sent to other destination
> rather than ring buffer only, we just need to register a new trace_export
> and implement its own .write() function for writing traces to storage.
>
> With this patch, only Function trace (trace type is TRACE_FN)
> is supported.
>
> Signed-off-by: Chunyan Zhang <zhang.chunyan@linaro.org>
> ---
> include/linux/trace.h | 28 +++++++++++
> kernel/trace/trace.c | 129 +++++++++++++++++++++++++++++++++++++++++++++++++-
> 2 files changed, 156 insertions(+), 1 deletion(-)
> create mode 100644 include/linux/trace.h
>
> diff --git a/include/linux/trace.h b/include/linux/trace.h
> new file mode 100644
> index 0000000..9330a58
> --- /dev/null
> +++ b/include/linux/trace.h
> @@ -0,0 +1,28 @@
> +#ifndef _LINUX_TRACE_H
> +#define _LINUX_TRACE_H
> +
> +#ifdef CONFIG_TRACING
> +/*
> + * The trace export - an export of Ftrace output. The trace_export
> + * can process traces and export them to a registered destination as
> + * an addition to the current only output of Ftrace - i.e. ring buffer.
> + *
> + * If you want traces to be sent to some other place rather than ring
> + * buffer only, just need to register a new trace_export and implement
> + * its own .write() function for writing traces to the storage.
> + *
> + * next - pointer to the next trace_export
> + * write - copy traces which have been delt with ->commit() to
> + * the destination
> + */
> +struct trace_export {
> + struct trace_export __rcu *next;
> + void (*write)(const void *, unsigned int);
> +};
> +
> +int register_ftrace_export(struct trace_export *export);
> +int unregister_ftrace_export(struct trace_export *export);
> +
> +#endif /* CONFIG_TRACING */
> +
> +#endif /* _LINUX_TRACE_H */
> diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
> index 8696ce6..038291d 100644
> --- a/kernel/trace/trace.c
> +++ b/kernel/trace/trace.c
> @@ -40,6 +40,7 @@
> #include <linux/poll.h>
> #include <linux/nmi.h>
> #include <linux/fs.h>
> +#include <linux/trace.h>
> #include <linux/sched/rt.h>
>
> #include "trace.h"
> @@ -2128,6 +2129,129 @@ void trace_buffer_unlock_commit_regs(struct trace_array *tr,
> ftrace_trace_userstack(buffer, flags, pc);
> }
>
> +static void
> +trace_process_export(struct trace_export *export,
> + struct ring_buffer_event *event)
> +{
> + struct trace_entry *entry;
> + unsigned int size = 0;
> +
> + entry = ring_buffer_event_data(event);
> + size = ring_buffer_event_length(event);
> + export->write(entry, size);
> +}
> +
> +static DEFINE_MUTEX(ftrace_export_lock);
> +
> +static struct trace_export __rcu *ftrace_exports_list __read_mostly;
> +
> +static DEFINE_STATIC_KEY_FALSE(ftrace_exports_enabled);
> +
> +static inline void ftrace_exports_enable(void)
> +{
> + static_branch_enable(&ftrace_exports_enabled);
> +}
> +
> +static inline void ftrace_exports_disable(void)
> +{
> + static_branch_disable(&ftrace_exports_enabled);
> +}
> +
> +void ftrace_exports(struct ring_buffer_event *event)
I'm currently testing the patches, but is there a reason that
ftrace_exports() is not static?
-- Steve
> +{
> + struct trace_export *export;
> +
> + preempt_disable_notrace();
> +
> + export = rcu_dereference_raw_notrace(ftrace_exports_list);
> + while (export) {
> + trace_process_export(export, event);
> + export = rcu_dereference_raw_notrace(export->next);
> + }
> +
> + preempt_enable_notrace();
> +}
> +
> +static inline void
> +add_trace_export(struct trace_export **list, struct trace_export *export)
> +{
> + rcu_assign_pointer(export->next, *list);
> + /*
> + * We are entering export into the list but another
> + * CPU might be walking that list. We need to make sure
> + * the export->next pointer is valid before another CPU sees
> + * the export pointer included into the list.
> + */
> + rcu_assign_pointer(*list, export);
> +}
> +
> +static inline int
> +rm_trace_export(struct trace_export **list, struct trace_export *export)
> +{
> + struct trace_export **p;
> +
> + for (p = list; *p != NULL; p = &(*p)->next)
> + if (*p == export)
> + break;
> +
> + if (*p != export)
> + return -1;
> +
> + rcu_assign_pointer(*p, (*p)->next);
> +
> + return 0;
> +}
> +
> +static inline void
> +add_ftrace_export(struct trace_export **list, struct trace_export *export)
> +{
> + if (*list == NULL)
> + ftrace_exports_enable();
> +
> + add_trace_export(list, export);
> +}
> +
> +static inline int
> +rm_ftrace_export(struct trace_export **list, struct trace_export *export)
> +{
> + int ret;
> +
> + ret = rm_trace_export(list, export);
> + if (*list == NULL)
> + ftrace_exports_disable();
> +
> + return ret;
> +}
> +
> +int register_ftrace_export(struct trace_export *export)
> +{
> + if (WARN_ON_ONCE(!export->write))
> + return -1;
> +
> + mutex_lock(&ftrace_export_lock);
> +
> + add_ftrace_export(&ftrace_exports_list, export);
> +
> + mutex_unlock(&ftrace_export_lock);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(register_ftrace_export);
> +
> +int unregister_ftrace_export(struct trace_export *export)
> +{
> + int ret;
> +
> + mutex_lock(&ftrace_export_lock);
> +
> + ret = rm_ftrace_export(&ftrace_exports_list, export);
> +
> + mutex_unlock(&ftrace_export_lock);
> +
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(unregister_ftrace_export);
> +
> void
> trace_function(struct trace_array *tr,
> unsigned long ip, unsigned long parent_ip, unsigned long flags,
> @@ -2146,8 +2270,11 @@ trace_function(struct trace_array *tr,
> entry->ip = ip;
> entry->parent_ip = parent_ip;
>
> - if (!call_filter_check_discard(call, entry, buffer, event))
> + if (!call_filter_check_discard(call, entry, buffer, event)) {
> + if (static_branch_unlikely(&ftrace_exports_enabled))
> + ftrace_exports(event);
> __buffer_unlock_commit(buffer, event);
> + }
> }
>
> #ifdef CONFIG_STACKTRACE
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH V9 1/6] tracing: add a possibility of exporting function trace to other places instead of ring buffer only
2016-11-22 22:39 ` Steven Rostedt
@ 2016-11-23 2:27 ` Chunyan Zhang
2016-11-23 5:05 ` Chunyan Zhang
0 siblings, 1 reply; 10+ messages in thread
From: Chunyan Zhang @ 2016-11-23 2:27 UTC (permalink / raw)
To: Steven Rostedt
Cc: Mathieu Poirier, Alexander Shishkin, mingo, Mike Leach,
Tor Jeremiassen, philippe.langlais, Nicolas GUION, Felipe Balbi,
Lyra Zhang, linux-kernel, linux-arm-kernel
On 23 November 2016 at 06:39, Steven Rostedt <rostedt@goodmis.org> wrote:
> On Mon, 21 Nov 2016 15:57:18 +0800
> Chunyan Zhang <zhang.chunyan@linaro.org> wrote:
>
>> Currently Function traces can be only exported to ring buffer, this
>> patch added trace_export concept which can process traces and export
>> them to a registered destination as an addition to the current only
>> one output of Ftrace - i.e. ring buffer.
>>
>> In this way, if we want Function traces to be sent to other destination
>> rather than ring buffer only, we just need to register a new trace_export
>> and implement its own .write() function for writing traces to storage.
>>
>> With this patch, only Function trace (trace type is TRACE_FN)
>> is supported.
>>
>> Signed-off-by: Chunyan Zhang <zhang.chunyan@linaro.org>
>> ---
>> include/linux/trace.h | 28 +++++++++++
>> kernel/trace/trace.c | 129 +++++++++++++++++++++++++++++++++++++++++++++++++-
>> 2 files changed, 156 insertions(+), 1 deletion(-)
>> create mode 100644 include/linux/trace.h
>>
>> diff --git a/include/linux/trace.h b/include/linux/trace.h
>> new file mode 100644
>> index 0000000..9330a58
>> --- /dev/null
>> +++ b/include/linux/trace.h
>> @@ -0,0 +1,28 @@
>> +#ifndef _LINUX_TRACE_H
>> +#define _LINUX_TRACE_H
>> +
>> +#ifdef CONFIG_TRACING
>> +/*
>> + * The trace export - an export of Ftrace output. The trace_export
>> + * can process traces and export them to a registered destination as
>> + * an addition to the current only output of Ftrace - i.e. ring buffer.
>> + *
>> + * If you want traces to be sent to some other place rather than ring
>> + * buffer only, just need to register a new trace_export and implement
>> + * its own .write() function for writing traces to the storage.
>> + *
>> + * next - pointer to the next trace_export
>> + * write - copy traces which have been delt with ->commit() to
>> + * the destination
>> + */
>> +struct trace_export {
>> + struct trace_export __rcu *next;
>> + void (*write)(const void *, unsigned int);
>> +};
>> +
>> +int register_ftrace_export(struct trace_export *export);
>> +int unregister_ftrace_export(struct trace_export *export);
>> +
>> +#endif /* CONFIG_TRACING */
>> +
>> +#endif /* _LINUX_TRACE_H */
>> diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
>> index 8696ce6..038291d 100644
>> --- a/kernel/trace/trace.c
>> +++ b/kernel/trace/trace.c
>> @@ -40,6 +40,7 @@
>> #include <linux/poll.h>
>> #include <linux/nmi.h>
>> #include <linux/fs.h>
>> +#include <linux/trace.h>
>> #include <linux/sched/rt.h>
>>
>> #include "trace.h"
>> @@ -2128,6 +2129,129 @@ void trace_buffer_unlock_commit_regs(struct trace_array *tr,
>> ftrace_trace_userstack(buffer, flags, pc);
>> }
>>
>> +static void
>> +trace_process_export(struct trace_export *export,
>> + struct ring_buffer_event *event)
>> +{
>> + struct trace_entry *entry;
>> + unsigned int size = 0;
>> +
>> + entry = ring_buffer_event_data(event);
>> + size = ring_buffer_event_length(event);
>> + export->write(entry, size);
>> +}
>> +
>> +static DEFINE_MUTEX(ftrace_export_lock);
>> +
>> +static struct trace_export __rcu *ftrace_exports_list __read_mostly;
>> +
>> +static DEFINE_STATIC_KEY_FALSE(ftrace_exports_enabled);
>> +
>> +static inline void ftrace_exports_enable(void)
>> +{
>> + static_branch_enable(&ftrace_exports_enabled);
>> +}
>> +
>> +static inline void ftrace_exports_disable(void)
>> +{
>> + static_branch_disable(&ftrace_exports_enabled);
>> +}
>> +
>> +void ftrace_exports(struct ring_buffer_event *event)
>
> I'm currently testing the patches, but is there a reason that
> ftrace_exports() is not static?
At present ftrace_exports() is only used by function trace though,
but I hope it can be used by other traces when it needed.
So I didn't mark it with static, but if you think it should better be
static for the time being, I can revise that.
Thanks,
Chunyan
>
> -- Steve
>
>> +{
>> + struct trace_export *export;
>> +
>> + preempt_disable_notrace();
>> +
>> + export = rcu_dereference_raw_notrace(ftrace_exports_list);
>> + while (export) {
>> + trace_process_export(export, event);
>> + export = rcu_dereference_raw_notrace(export->next);
>> + }
>> +
>> + preempt_enable_notrace();
>> +}
>> +
>> +static inline void
>> +add_trace_export(struct trace_export **list, struct trace_export *export)
>> +{
>> + rcu_assign_pointer(export->next, *list);
>> + /*
>> + * We are entering export into the list but another
>> + * CPU might be walking that list. We need to make sure
>> + * the export->next pointer is valid before another CPU sees
>> + * the export pointer included into the list.
>> + */
>> + rcu_assign_pointer(*list, export);
>> +}
>> +
>> +static inline int
>> +rm_trace_export(struct trace_export **list, struct trace_export *export)
>> +{
>> + struct trace_export **p;
>> +
>> + for (p = list; *p != NULL; p = &(*p)->next)
>> + if (*p == export)
>> + break;
>> +
>> + if (*p != export)
>> + return -1;
>> +
>> + rcu_assign_pointer(*p, (*p)->next);
>> +
>> + return 0;
>> +}
>> +
>> +static inline void
>> +add_ftrace_export(struct trace_export **list, struct trace_export *export)
>> +{
>> + if (*list == NULL)
>> + ftrace_exports_enable();
>> +
>> + add_trace_export(list, export);
>> +}
>> +
>> +static inline int
>> +rm_ftrace_export(struct trace_export **list, struct trace_export *export)
>> +{
>> + int ret;
>> +
>> + ret = rm_trace_export(list, export);
>> + if (*list == NULL)
>> + ftrace_exports_disable();
>> +
>> + return ret;
>> +}
>> +
>> +int register_ftrace_export(struct trace_export *export)
>> +{
>> + if (WARN_ON_ONCE(!export->write))
>> + return -1;
>> +
>> + mutex_lock(&ftrace_export_lock);
>> +
>> + add_ftrace_export(&ftrace_exports_list, export);
>> +
>> + mutex_unlock(&ftrace_export_lock);
>> +
>> + return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(register_ftrace_export);
>> +
>> +int unregister_ftrace_export(struct trace_export *export)
>> +{
>> + int ret;
>> +
>> + mutex_lock(&ftrace_export_lock);
>> +
>> + ret = rm_ftrace_export(&ftrace_exports_list, export);
>> +
>> + mutex_unlock(&ftrace_export_lock);
>> +
>> + return ret;
>> +}
>> +EXPORT_SYMBOL_GPL(unregister_ftrace_export);
>> +
>> void
>> trace_function(struct trace_array *tr,
>> unsigned long ip, unsigned long parent_ip, unsigned long flags,
>> @@ -2146,8 +2270,11 @@ trace_function(struct trace_array *tr,
>> entry->ip = ip;
>> entry->parent_ip = parent_ip;
>>
>> - if (!call_filter_check_discard(call, entry, buffer, event))
>> + if (!call_filter_check_discard(call, entry, buffer, event)) {
>> + if (static_branch_unlikely(&ftrace_exports_enabled))
>> + ftrace_exports(event);
>> __buffer_unlock_commit(buffer, event);
>> + }
>> }
>>
>> #ifdef CONFIG_STACKTRACE
>
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH V9 1/6] tracing: add a possibility of exporting function trace to other places instead of ring buffer only
2016-11-23 2:27 ` Chunyan Zhang
@ 2016-11-23 5:05 ` Chunyan Zhang
0 siblings, 0 replies; 10+ messages in thread
From: Chunyan Zhang @ 2016-11-23 5:05 UTC (permalink / raw)
To: Steven Rostedt
Cc: Mathieu Poirier, Alexander Shishkin, mingo, Mike Leach,
Tor Jeremiassen, philippe.langlais, Nicolas GUION, Felipe Balbi,
Lyra Zhang, linux-kernel, linux-arm-kernel
Hi Steve,
Actually I had been keeping the idea that we would need to export most
kinds of traces rather than function trace only to somewhere else, say
STM, that's also why I made STM_SOURCE_FTRACE depending on TRACING
which was later changed to FUNCTION_TRACER according to you advice.
Thanks,
Chunyan
On 23 November 2016 at 10:27, Chunyan Zhang <zhang.chunyan@linaro.org> wrote:
> On 23 November 2016 at 06:39, Steven Rostedt <rostedt@goodmis.org> wrote:
>> On Mon, 21 Nov 2016 15:57:18 +0800
>> Chunyan Zhang <zhang.chunyan@linaro.org> wrote:
>>
>>> Currently Function traces can be only exported to ring buffer, this
>>> patch added trace_export concept which can process traces and export
>>> them to a registered destination as an addition to the current only
>>> one output of Ftrace - i.e. ring buffer.
>>>
>>> In this way, if we want Function traces to be sent to other destination
>>> rather than ring buffer only, we just need to register a new trace_export
>>> and implement its own .write() function for writing traces to storage.
>>>
>>> With this patch, only Function trace (trace type is TRACE_FN)
>>> is supported.
>>>
>>> Signed-off-by: Chunyan Zhang <zhang.chunyan@linaro.org>
>>> ---
>>> include/linux/trace.h | 28 +++++++++++
>>> kernel/trace/trace.c | 129 +++++++++++++++++++++++++++++++++++++++++++++++++-
>>> 2 files changed, 156 insertions(+), 1 deletion(-)
>>> create mode 100644 include/linux/trace.h
>>>
>>> diff --git a/include/linux/trace.h b/include/linux/trace.h
>>> new file mode 100644
>>> index 0000000..9330a58
>>> --- /dev/null
>>> +++ b/include/linux/trace.h
>>> @@ -0,0 +1,28 @@
>>> +#ifndef _LINUX_TRACE_H
>>> +#define _LINUX_TRACE_H
>>> +
>>> +#ifdef CONFIG_TRACING
>>> +/*
>>> + * The trace export - an export of Ftrace output. The trace_export
>>> + * can process traces and export them to a registered destination as
>>> + * an addition to the current only output of Ftrace - i.e. ring buffer.
>>> + *
>>> + * If you want traces to be sent to some other place rather than ring
>>> + * buffer only, just need to register a new trace_export and implement
>>> + * its own .write() function for writing traces to the storage.
>>> + *
>>> + * next - pointer to the next trace_export
>>> + * write - copy traces which have been delt with ->commit() to
>>> + * the destination
>>> + */
>>> +struct trace_export {
>>> + struct trace_export __rcu *next;
>>> + void (*write)(const void *, unsigned int);
>>> +};
>>> +
>>> +int register_ftrace_export(struct trace_export *export);
>>> +int unregister_ftrace_export(struct trace_export *export);
>>> +
>>> +#endif /* CONFIG_TRACING */
>>> +
>>> +#endif /* _LINUX_TRACE_H */
>>> diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
>>> index 8696ce6..038291d 100644
>>> --- a/kernel/trace/trace.c
>>> +++ b/kernel/trace/trace.c
>>> @@ -40,6 +40,7 @@
>>> #include <linux/poll.h>
>>> #include <linux/nmi.h>
>>> #include <linux/fs.h>
>>> +#include <linux/trace.h>
>>> #include <linux/sched/rt.h>
>>>
>>> #include "trace.h"
>>> @@ -2128,6 +2129,129 @@ void trace_buffer_unlock_commit_regs(struct trace_array *tr,
>>> ftrace_trace_userstack(buffer, flags, pc);
>>> }
>>>
>>> +static void
>>> +trace_process_export(struct trace_export *export,
>>> + struct ring_buffer_event *event)
>>> +{
>>> + struct trace_entry *entry;
>>> + unsigned int size = 0;
>>> +
>>> + entry = ring_buffer_event_data(event);
>>> + size = ring_buffer_event_length(event);
>>> + export->write(entry, size);
>>> +}
>>> +
>>> +static DEFINE_MUTEX(ftrace_export_lock);
>>> +
>>> +static struct trace_export __rcu *ftrace_exports_list __read_mostly;
>>> +
>>> +static DEFINE_STATIC_KEY_FALSE(ftrace_exports_enabled);
>>> +
>>> +static inline void ftrace_exports_enable(void)
>>> +{
>>> + static_branch_enable(&ftrace_exports_enabled);
>>> +}
>>> +
>>> +static inline void ftrace_exports_disable(void)
>>> +{
>>> + static_branch_disable(&ftrace_exports_enabled);
>>> +}
>>> +
>>> +void ftrace_exports(struct ring_buffer_event *event)
>>
>> I'm currently testing the patches, but is there a reason that
>> ftrace_exports() is not static?
>
> At present ftrace_exports() is only used by function trace though,
> but I hope it can be used by other traces when it needed.
> So I didn't mark it with static, but if you think it should better be
> static for the time being, I can revise that.
>
> Thanks,
> Chunyan
>
>
>>
>> -- Steve
>>
>>> +{
>>> + struct trace_export *export;
>>> +
>>> + preempt_disable_notrace();
>>> +
>>> + export = rcu_dereference_raw_notrace(ftrace_exports_list);
>>> + while (export) {
>>> + trace_process_export(export, event);
>>> + export = rcu_dereference_raw_notrace(export->next);
>>> + }
>>> +
>>> + preempt_enable_notrace();
>>> +}
>>> +
>>> +static inline void
>>> +add_trace_export(struct trace_export **list, struct trace_export *export)
>>> +{
>>> + rcu_assign_pointer(export->next, *list);
>>> + /*
>>> + * We are entering export into the list but another
>>> + * CPU might be walking that list. We need to make sure
>>> + * the export->next pointer is valid before another CPU sees
>>> + * the export pointer included into the list.
>>> + */
>>> + rcu_assign_pointer(*list, export);
>>> +}
>>> +
>>> +static inline int
>>> +rm_trace_export(struct trace_export **list, struct trace_export *export)
>>> +{
>>> + struct trace_export **p;
>>> +
>>> + for (p = list; *p != NULL; p = &(*p)->next)
>>> + if (*p == export)
>>> + break;
>>> +
>>> + if (*p != export)
>>> + return -1;
>>> +
>>> + rcu_assign_pointer(*p, (*p)->next);
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static inline void
>>> +add_ftrace_export(struct trace_export **list, struct trace_export *export)
>>> +{
>>> + if (*list == NULL)
>>> + ftrace_exports_enable();
>>> +
>>> + add_trace_export(list, export);
>>> +}
>>> +
>>> +static inline int
>>> +rm_ftrace_export(struct trace_export **list, struct trace_export *export)
>>> +{
>>> + int ret;
>>> +
>>> + ret = rm_trace_export(list, export);
>>> + if (*list == NULL)
>>> + ftrace_exports_disable();
>>> +
>>> + return ret;
>>> +}
>>> +
>>> +int register_ftrace_export(struct trace_export *export)
>>> +{
>>> + if (WARN_ON_ONCE(!export->write))
>>> + return -1;
>>> +
>>> + mutex_lock(&ftrace_export_lock);
>>> +
>>> + add_ftrace_export(&ftrace_exports_list, export);
>>> +
>>> + mutex_unlock(&ftrace_export_lock);
>>> +
>>> + return 0;
>>> +}
>>> +EXPORT_SYMBOL_GPL(register_ftrace_export);
>>> +
>>> +int unregister_ftrace_export(struct trace_export *export)
>>> +{
>>> + int ret;
>>> +
>>> + mutex_lock(&ftrace_export_lock);
>>> +
>>> + ret = rm_ftrace_export(&ftrace_exports_list, export);
>>> +
>>> + mutex_unlock(&ftrace_export_lock);
>>> +
>>> + return ret;
>>> +}
>>> +EXPORT_SYMBOL_GPL(unregister_ftrace_export);
>>> +
>>> void
>>> trace_function(struct trace_array *tr,
>>> unsigned long ip, unsigned long parent_ip, unsigned long flags,
>>> @@ -2146,8 +2270,11 @@ trace_function(struct trace_array *tr,
>>> entry->ip = ip;
>>> entry->parent_ip = parent_ip;
>>>
>>> - if (!call_filter_check_discard(call, entry, buffer, event))
>>> + if (!call_filter_check_discard(call, entry, buffer, event)) {
>>> + if (static_branch_unlikely(&ftrace_exports_enabled))
>>> + ftrace_exports(event);
>>> __buffer_unlock_commit(buffer, event);
>>> + }
>>> }
>>>
>>> #ifdef CONFIG_STACKTRACE
>>
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH V9 2/6] stm class: ftrace: Add ftrace-export-over-stm driver
2016-11-21 7:57 [PATCH V9 0/6] Integration of function trace with System Trace IP blocks Chunyan Zhang
2016-11-21 7:57 ` [PATCH V9 1/6] tracing: add a possibility of exporting function trace to other places instead of ring buffer only Chunyan Zhang
@ 2016-11-21 7:57 ` Chunyan Zhang
2016-11-21 7:57 ` [PATCH V9 3/6] coresight: Mark stm_generic_packet() with notrace Chunyan Zhang
` (3 subsequent siblings)
5 siblings, 0 replies; 10+ messages in thread
From: Chunyan Zhang @ 2016-11-21 7:57 UTC (permalink / raw)
To: rostedt, mathieu.poirier, alexander.shishkin, mingo
Cc: mike.leach, tor, philippe.langlais, nicolas.guion, felipe.balbi,
zhang.lyra, linux-kernel, linux-arm-kernel
This patch adds a driver that models itself as an stm_source called
stm_ftrace. Once the stm device and stm_ftrace have been linked via
sysfs, the driver registers itself as a trace_export and everything
passed to the interface from Ftrace subsystem will end up in the STM
trace engine.
Signed-off-by: Chunyan Zhang <zhang.chunyan@linaro.org>
Acked-by: Alexander Shishkin <alexander.shishkin@linux.intel.com>
---
drivers/hwtracing/stm/Kconfig | 11 ++++++
drivers/hwtracing/stm/Makefile | 2 +
drivers/hwtracing/stm/ftrace.c | 87 ++++++++++++++++++++++++++++++++++++++++++
3 files changed, 100 insertions(+)
create mode 100644 drivers/hwtracing/stm/ftrace.c
diff --git a/drivers/hwtracing/stm/Kconfig b/drivers/hwtracing/stm/Kconfig
index 847a39b..723e2d9 100644
--- a/drivers/hwtracing/stm/Kconfig
+++ b/drivers/hwtracing/stm/Kconfig
@@ -39,4 +39,15 @@ config STM_SOURCE_HEARTBEAT
If you want to send heartbeat messages over STM devices,
say Y.
+config STM_SOURCE_FTRACE
+ tristate "Copy the output from kernel Ftrace to STM engine"
+ depends on FUNCTION_TRACER
+ help
+ This option can be used to copy the output from kernel Ftrace
+ to STM engine. Enabling this option will introduce a slight
+ timing effect.
+
+ If you want to send kernel Ftrace messages over STM devices,
+ say Y.
+
endif
diff --git a/drivers/hwtracing/stm/Makefile b/drivers/hwtracing/stm/Makefile
index a9ce3d4..3abd84c 100644
--- a/drivers/hwtracing/stm/Makefile
+++ b/drivers/hwtracing/stm/Makefile
@@ -6,6 +6,8 @@ obj-$(CONFIG_STM_DUMMY) += dummy_stm.o
obj-$(CONFIG_STM_SOURCE_CONSOLE) += stm_console.o
obj-$(CONFIG_STM_SOURCE_HEARTBEAT) += stm_heartbeat.o
+obj-$(CONFIG_STM_SOURCE_FTRACE) += stm_ftrace.o
stm_console-y := console.o
stm_heartbeat-y := heartbeat.o
+stm_ftrace-y := ftrace.o
diff --git a/drivers/hwtracing/stm/ftrace.c b/drivers/hwtracing/stm/ftrace.c
new file mode 100644
index 0000000..bd126a7
--- /dev/null
+++ b/drivers/hwtracing/stm/ftrace.c
@@ -0,0 +1,87 @@
+/*
+ * Simple kernel driver to link kernel Ftrace and an STM device
+ * Copyright (c) 2016, Linaro Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * STM Ftrace will be registered as a trace_export.
+ */
+
+#include <linux/module.h>
+#include <linux/stm.h>
+#include <linux/trace.h>
+
+#define STM_FTRACE_NR_CHANNELS 1
+#define STM_FTRACE_CHAN 0
+
+static int stm_ftrace_link(struct stm_source_data *data);
+static void stm_ftrace_unlink(struct stm_source_data *data);
+
+static struct stm_ftrace {
+ struct stm_source_data data;
+ struct trace_export ftrace;
+} stm_ftrace = {
+ .data = {
+ .name = "ftrace",
+ .nr_chans = STM_FTRACE_NR_CHANNELS,
+ .link = stm_ftrace_link,
+ .unlink = stm_ftrace_unlink,
+ },
+};
+
+/**
+ * stm_ftrace_write() - write data to STM via 'stm_ftrace' source
+ * @buf: buffer containing the data packet
+ * @len: length of the data packet
+ */
+static void notrace
+stm_ftrace_write(const void *buf, unsigned int len)
+{
+ stm_source_write(&stm_ftrace.data, STM_FTRACE_CHAN, buf, len);
+}
+
+static int stm_ftrace_link(struct stm_source_data *data)
+{
+ struct stm_ftrace *sf = container_of(data, struct stm_ftrace, data);
+
+ sf->ftrace.write = stm_ftrace_write;
+
+ return register_ftrace_export(&sf->ftrace);
+}
+
+static void stm_ftrace_unlink(struct stm_source_data *data)
+{
+ struct stm_ftrace *sf = container_of(data, struct stm_ftrace, data);
+
+ unregister_ftrace_export(&sf->ftrace);
+}
+
+static int __init stm_ftrace_init(void)
+{
+ int ret;
+
+ ret = stm_source_register_device(NULL, &stm_ftrace.data);
+ if (ret)
+ pr_err("Failed to register stm_source - ftrace.\n");
+
+ return ret;
+}
+
+static void __exit stm_ftrace_exit(void)
+{
+ stm_source_unregister_device(&stm_ftrace.data);
+}
+
+module_init(stm_ftrace_init);
+module_exit(stm_ftrace_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("stm_ftrace driver");
+MODULE_AUTHOR("Chunyan Zhang <zhang.chunyan@linaro.org>");
--
2.7.4
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH V9 3/6] coresight: Mark stm_generic_packet() with notrace
2016-11-21 7:57 [PATCH V9 0/6] Integration of function trace with System Trace IP blocks Chunyan Zhang
2016-11-21 7:57 ` [PATCH V9 1/6] tracing: add a possibility of exporting function trace to other places instead of ring buffer only Chunyan Zhang
2016-11-21 7:57 ` [PATCH V9 2/6] stm class: ftrace: Add ftrace-export-over-stm driver Chunyan Zhang
@ 2016-11-21 7:57 ` Chunyan Zhang
2016-11-21 7:57 ` [PATCH V9 4/6] intel_th: Mark sth_stm_packet() " Chunyan Zhang
` (2 subsequent siblings)
5 siblings, 0 replies; 10+ messages in thread
From: Chunyan Zhang @ 2016-11-21 7:57 UTC (permalink / raw)
To: rostedt, mathieu.poirier, alexander.shishkin, mingo
Cc: mike.leach, tor, philippe.langlais, nicolas.guion, felipe.balbi,
zhang.lyra, linux-kernel, linux-arm-kernel
If CONFIG_STM_SOURCE_FTRACE is selected, Function trace data can be
writen to sink via STM, all functions that related to writing data
packets to STM should be marked 'notrace' to avoid being traced by
Ftrace, otherwise the program would stall into an endless loop.
Signed-off-by: Chunyan Zhang <zhang.chunyan@linaro.org>
Acked-by: Alexander Shishkin <alexander.shishkin@linux.intel.com>
---
drivers/hwtracing/coresight/coresight-stm.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/hwtracing/coresight/coresight-stm.c b/drivers/hwtracing/coresight/coresight-stm.c
index 49e0f1b..b7543bd 100644
--- a/drivers/hwtracing/coresight/coresight-stm.c
+++ b/drivers/hwtracing/coresight/coresight-stm.c
@@ -406,7 +406,7 @@ static long stm_generic_set_options(struct stm_data *stm_data,
return 0;
}
-static ssize_t stm_generic_packet(struct stm_data *stm_data,
+static ssize_t notrace stm_generic_packet(struct stm_data *stm_data,
unsigned int master,
unsigned int channel,
unsigned int packet,
--
2.7.4
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH V9 4/6] intel_th: Mark sth_stm_packet() with notrace
2016-11-21 7:57 [PATCH V9 0/6] Integration of function trace with System Trace IP blocks Chunyan Zhang
` (2 preceding siblings ...)
2016-11-21 7:57 ` [PATCH V9 3/6] coresight: Mark stm_generic_packet() with notrace Chunyan Zhang
@ 2016-11-21 7:57 ` Chunyan Zhang
2016-11-21 7:57 ` [PATCH V9 5/6] stm dummy: Mark dummy_stm_packet() " Chunyan Zhang
2016-11-21 7:57 ` [PATCH V9 6/6] stm: Mark the functions of writing STM " Chunyan Zhang
5 siblings, 0 replies; 10+ messages in thread
From: Chunyan Zhang @ 2016-11-21 7:57 UTC (permalink / raw)
To: rostedt, mathieu.poirier, alexander.shishkin, mingo
Cc: mike.leach, tor, philippe.langlais, nicolas.guion, felipe.balbi,
zhang.lyra, linux-kernel, linux-arm-kernel
If CONFIG_STM_SOURCE_FTRACE is selected, Function trace data can be
writen to sink via STM, all functions that related to writing data
packets to STM should be marked 'notrace' to avoid being traced by
Ftrace, otherwise the program would stall into an endless loop.
Signed-off-by: Chunyan Zhang <zhang.chunyan@linaro.org>
Acked-by: Alexander Shishkin <alexander.shishkin@linux.intel.com>
---
drivers/hwtracing/intel_th/sth.c | 11 +++++++----
1 file changed, 7 insertions(+), 4 deletions(-)
diff --git a/drivers/hwtracing/intel_th/sth.c b/drivers/hwtracing/intel_th/sth.c
index e1aee61..b034446 100644
--- a/drivers/hwtracing/intel_th/sth.c
+++ b/drivers/hwtracing/intel_th/sth.c
@@ -67,10 +67,13 @@ static void sth_iowrite(void __iomem *dest, const unsigned char *payload,
}
}
-static ssize_t sth_stm_packet(struct stm_data *stm_data, unsigned int master,
- unsigned int channel, unsigned int packet,
- unsigned int flags, unsigned int size,
- const unsigned char *payload)
+static ssize_t notrace sth_stm_packet(struct stm_data *stm_data,
+ unsigned int master,
+ unsigned int channel,
+ unsigned int packet,
+ unsigned int flags,
+ unsigned int size,
+ const unsigned char *payload)
{
struct sth_device *sth = container_of(stm_data, struct sth_device, stm);
struct intel_th_channel __iomem *out =
--
2.7.4
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH V9 5/6] stm dummy: Mark dummy_stm_packet() with notrace
2016-11-21 7:57 [PATCH V9 0/6] Integration of function trace with System Trace IP blocks Chunyan Zhang
` (3 preceding siblings ...)
2016-11-21 7:57 ` [PATCH V9 4/6] intel_th: Mark sth_stm_packet() " Chunyan Zhang
@ 2016-11-21 7:57 ` Chunyan Zhang
2016-11-21 7:57 ` [PATCH V9 6/6] stm: Mark the functions of writing STM " Chunyan Zhang
5 siblings, 0 replies; 10+ messages in thread
From: Chunyan Zhang @ 2016-11-21 7:57 UTC (permalink / raw)
To: rostedt, mathieu.poirier, alexander.shishkin, mingo
Cc: mike.leach, tor, philippe.langlais, nicolas.guion, felipe.balbi,
zhang.lyra, linux-kernel, linux-arm-kernel
If CONFIG_STM_SOURCE_FTRACE is selected, Function trace data can be
writen to sink via STM, all functions that related to writing data
packets to STM should be marked 'notrace' to avoid being traced by
Ftrace, otherwise the program would stall into an endless loop.
Signed-off-by: Chunyan Zhang <zhang.chunyan@linaro.org>
Acked-by: Alexander Shishkin <alexander.shishkin@linux.intel.com>
---
drivers/hwtracing/stm/dummy_stm.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/hwtracing/stm/dummy_stm.c b/drivers/hwtracing/stm/dummy_stm.c
index a86612d..c5f94ca 100644
--- a/drivers/hwtracing/stm/dummy_stm.c
+++ b/drivers/hwtracing/stm/dummy_stm.c
@@ -21,7 +21,7 @@
#include <linux/slab.h>
#include <linux/stm.h>
-static ssize_t
+static ssize_t notrace
dummy_stm_packet(struct stm_data *stm_data, unsigned int master,
unsigned int channel, unsigned int packet, unsigned int flags,
unsigned int size, const unsigned char *payload)
--
2.7.4
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH V9 6/6] stm: Mark the functions of writing STM with notrace
2016-11-21 7:57 [PATCH V9 0/6] Integration of function trace with System Trace IP blocks Chunyan Zhang
` (4 preceding siblings ...)
2016-11-21 7:57 ` [PATCH V9 5/6] stm dummy: Mark dummy_stm_packet() " Chunyan Zhang
@ 2016-11-21 7:57 ` Chunyan Zhang
5 siblings, 0 replies; 10+ messages in thread
From: Chunyan Zhang @ 2016-11-21 7:57 UTC (permalink / raw)
To: rostedt, mathieu.poirier, alexander.shishkin, mingo
Cc: mike.leach, tor, philippe.langlais, nicolas.guion, felipe.balbi,
zhang.lyra, linux-kernel, linux-arm-kernel
If CONFIG_STM_SOURCE_FTRACE is selected, Function trace data can be
writen to sink via STM, all functions that related to writing data
packets to STM should be marked 'notrace' to avoid being traced by
Ftrace, otherwise the program would stall into an endless loop.
Signed-off-by: Chunyan Zhang <zhang.chunyan@linaro.org>
Acked-by: Alexander Shishkin <alexander.shishkin@linux.intel.com>
---
drivers/hwtracing/stm/core.c | 7 ++++---
include/linux/stm.h | 4 ++--
2 files changed, 6 insertions(+), 5 deletions(-)
diff --git a/drivers/hwtracing/stm/core.c b/drivers/hwtracing/stm/core.c
index 51f81d6..37d3bcb 100644
--- a/drivers/hwtracing/stm/core.c
+++ b/drivers/hwtracing/stm/core.c
@@ -425,7 +425,7 @@ static int stm_file_assign(struct stm_file *stmf, char *id, unsigned int width)
return ret;
}
-static ssize_t stm_write(struct stm_data *data, unsigned int master,
+static ssize_t notrace stm_write(struct stm_data *data, unsigned int master,
unsigned int channel, const char *buf, size_t count)
{
unsigned int flags = STP_PACKET_TIMESTAMPED;
@@ -1121,8 +1121,9 @@ void stm_source_unregister_device(struct stm_source_data *data)
}
EXPORT_SYMBOL_GPL(stm_source_unregister_device);
-int stm_source_write(struct stm_source_data *data, unsigned int chan,
- const char *buf, size_t count)
+int notrace stm_source_write(struct stm_source_data *data,
+ unsigned int chan,
+ const char *buf, size_t count)
{
struct stm_source_device *src = data->src;
struct stm_device *stm;
diff --git a/include/linux/stm.h b/include/linux/stm.h
index 8369d8a..210ff22 100644
--- a/include/linux/stm.h
+++ b/include/linux/stm.h
@@ -133,7 +133,7 @@ int stm_source_register_device(struct device *parent,
struct stm_source_data *data);
void stm_source_unregister_device(struct stm_source_data *data);
-int stm_source_write(struct stm_source_data *data, unsigned int chan,
- const char *buf, size_t count);
+int notrace stm_source_write(struct stm_source_data *data, unsigned int chan,
+ const char *buf, size_t count);
#endif /* _STM_H_ */
--
2.7.4
^ permalink raw reply related [flat|nested] 10+ messages in thread