Signed-off-by: Steven Rostedt (Google) <[email protected]>
---
Documentation/trace/ftrace.rst | 15 +++++++++
kernel/trace/trace.c | 60 +++++++++++++++++++++++++++++++---
kernel/trace/trace.h | 1 +
3 files changed, 71 insertions(+), 5 deletions(-)
diff --git a/Documentation/trace/ftrace.rst b/Documentation/trace/ftrace.rst
index c9e88bf65709..6b3915139347 100644
--- a/Documentation/trace/ftrace.rst
+++ b/Documentation/trace/ftrace.rst
@@ -1205,6 +1205,21 @@ Here are the available options:
default instance. The only way the top level instance has this flag
cleared, is by it being set in another instance.
+ redirect_trace_marker
+ This is similar to the trace_printk_dest option above, but it works
+ for the top level trace_marker (/sys/kernel/tracing/trace_marker).
+ If there are applications that hard code writing into the top level
+ trace_marker file, and the tooling would like it to go into an
+ instance, this option can be used. Create an instance and set this
+ option, and then all writes into the top level trace_marker file will
+ be redirected into that instance.
+
+ This flag cannot be cleared by the top level instance, as it is the
+ default instance. The only way the top level instance has this flag
+ cleared, is by it being set in another instance. By setting this flag
+ in the top level instance, it will put the writing into the
+ trace_marker file back into the top level instance buffer.
+
annotate
It is sometimes confusing when the CPU buffers are full
and one CPU buffer had a lot of events recently, thus
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index 8ddf6b17215c..ec7e15434759 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -493,7 +493,8 @@ EXPORT_SYMBOL_GPL(unregister_ftrace_export);
TRACE_ITER_ANNOTATE | TRACE_ITER_CONTEXT_INFO | \
TRACE_ITER_RECORD_CMD | TRACE_ITER_OVERWRITE | \
TRACE_ITER_IRQ_INFO | TRACE_ITER_MARKERS | \
- TRACE_ITER_HASH_PTR | TRACE_ITER_TRACE_PRINTK)
+ TRACE_ITER_HASH_PTR | TRACE_ITER_TRACE_PRINTK | \
+ TRACE_ITER_REDIRECT_MARKER)
/* trace_options that are only supported by global_trace */
#define TOP_LEVEL_TRACE_FLAGS (TRACE_ITER_PRINTK | \
@@ -501,7 +502,8 @@ EXPORT_SYMBOL_GPL(unregister_ftrace_export);
/* trace_flags that are default zero for instances */
#define ZEROED_TRACE_FLAGS \
- (TRACE_ITER_EVENT_FORK | TRACE_ITER_FUNC_FORK | TRACE_ITER_TRACE_PRINTK)
+ (TRACE_ITER_EVENT_FORK | TRACE_ITER_FUNC_FORK | TRACE_ITER_TRACE_PRINTK
| \
+ TRACE_ITER_REDIRECT_MARKER)
/*
* The global_trace is the descriptor that holds the top-level tracing
@@ -512,6 +514,7 @@ static struct trace_array global_trace = {
};
static struct trace_array *printk_trace = &global_trace;
+static struct trace_array *marker_trace = &global_trace;
static __always_inline bool printk_binsafe(struct trace_array *tr)
{
@@ -534,6 +537,16 @@ static void update_printk_trace(struct trace_array *tr)
tr->trace_flags |= TRACE_ITER_TRACE_PRINTK;
}
+static void update_marker_trace(struct trace_array *tr)
+{
+ if (marker_trace == tr)
+ return;
+
+ marker_trace->trace_flags &= ~TRACE_ITER_REDIRECT_MARKER;
+ marker_trace = tr;
+ tr->trace_flags |= TRACE_ITER_REDIRECT_MARKER;
+}
+
void trace_set_ring_buffer_expanded(struct trace_array *tr)
{
if (!tr)
@@ -4755,8 +4768,22 @@ int tracing_single_release_file_tr(struct inode *inode,
struct file *filp)
static int tracing_mark_open(struct inode *inode, struct file *filp)
{
+ struct trace_array *tr = inode->i_private;
+ int ret;
+
stream_open(inode, filp);
- return tracing_open_generic_tr(inode, filp);
+
+ /* The top level marker can have it redirected to an instance */
+ if (tr == &global_trace)
+ tr = marker_trace;
+
+ ret = tracing_check_open_get_tr(tr);
+ if (ret)
+ return ret;
+
+ filp->private_data = tr;
+
+ return 0;
}
static int tracing_release(struct inode *inode, struct file *file)
@@ -4799,7 +4826,7 @@ static int tracing_release(struct inode *inode, struct
file *file)
int tracing_release_generic_tr(struct inode *inode, struct file *file)
{
- struct trace_array *tr = inode->i_private;
+ struct trace_array *tr = file->private_data;
trace_array_put(tr);
return 0;
@@ -5189,7 +5216,8 @@ int set_tracer_flag(struct trace_array *tr, unsigned int
mask, int enabled)
{
if ((mask == TRACE_ITER_RECORD_TGID) ||
(mask == TRACE_ITER_RECORD_CMD) ||
- (mask == TRACE_ITER_TRACE_PRINTK))
+ (mask == TRACE_ITER_TRACE_PRINTK) ||
+ (mask == TRACE_ITER_REDIRECT_MARKER))
lockdep_assert_held(&event_mutex);
/* do nothing if flag is already set */
@@ -5220,6 +5248,25 @@ int set_tracer_flag(struct trace_array *tr, unsigned int
mask, int enabled)
}
}
+ if (mask == TRACE_ITER_REDIRECT_MARKER) {
+ if (enabled) {
+ update_marker_trace(tr);
+ } else {
+ /*
+ * The global_trace cannot clear this.
+ * It's flag only gets cleared if another instance sets
it.
+ */
+ if (marker_trace == &global_trace)
+ return -EINVAL;
+ /*
+ * An instance must always have it set.
+ * by default, that's the global_trace instane.
+ */
+ if (marker_trace == tr)
+ update_marker_trace(&global_trace);
+ }
+ }
+
if (enabled)
tr->trace_flags |= mask;
else
@@ -9897,6 +9944,9 @@ static int __remove_instance(struct trace_array *tr)
if (printk_trace == tr)
update_printk_trace(&global_trace);
+ if (marker_trace == tr)
+ update_marker_trace(&global_trace);
+
tracing_set_nop(tr);
clear_ftrace_function_probes(tr);
event_trace_del_tracer(tr);
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
index 79be1995db44..602457eda8e8 100644
--- a/kernel/trace/trace.h
+++ b/kernel/trace/trace.h
@@ -1368,6 +1368,7 @@ extern int trace_get_user(struct trace_parser *parser,
const char __user *ubuf,
C(MARKERS, "markers"), \
C(EVENT_FORK, "event-fork"), \
C(TRACE_PRINTK, "trace_printk_dest"), \
+ C(REDIRECT_MARKER, "redirect_trace_marker"),\
C(PAUSE_ON_TRACE, "pause-on-trace"), \
C(HASH_PTR, "hash-ptr"), /* Print hashed pointer
*/ \
FUNCTION_FLAGS \