[PATCH 12/15] trace: Export anonymous tracing

Chris Wilson chris at chris-wilson.co.uk
Mon Jan 18 23:01:30 UTC 2021


To facilitate construction of per-client event ringbuffers, in
particular for a per-client debug and error report log, it would be
extremely useful to create an anonymous file that can be handed to
userspace so that it can see its and only its events. trace already
provides a means of encapsulating the trace ringbuffer into a struct
file that can be opened via the tracefs, and so with a couple of minor
tweaks can provide the same access via an anonymous inode.

Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>
Cc: Steven Rostedt (VMware) <rostedt at goodmis.org>
---
 include/linux/trace.h |  20 ++++++
 kernel/trace/trace.c  | 142 +++++++++++++++++++++++++++++++-----------
 2 files changed, 127 insertions(+), 35 deletions(-)

diff --git a/include/linux/trace.h b/include/linux/trace.h
index 886a4ffd9d45..69ad44ad288b 100644
--- a/include/linux/trace.h
+++ b/include/linux/trace.h
@@ -38,8 +38,28 @@ int trace_array_printk(struct trace_array *tr, unsigned long ip,
 		const char *fmt, ...);
 int trace_array_init_printk(struct trace_array *tr);
 void trace_array_put(struct trace_array *tr);
+struct trace_array *trace_array_create(void);
 struct trace_array *trace_array_get_by_name(const char *name);
 int trace_array_destroy(struct trace_array *tr);
+
+int anon_trace_getfd(const char *name, struct trace_array *tr);
+
+#else
+
+static inline struct trace_array *trace_array_create(void) { return NULL; }
+static inline int trace_array_destroy(struct trace_array *tr) { return 0; }
+
+static inline int trace_array_printk(struct trace_array *tr, unsigned long ip,
+				     const char *fmt, ...)
+{
+	return 0;
+}
+
+static inline int anon_trace_getfd(const char *name, struct trace_array *tr)
+{
+       	return -1;
+}
+
 #endif	/* CONFIG_TRACING */
 
 #endif	/* _LINUX_TRACE_H */
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index ce259a7e3c3a..d006b585c871 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -48,6 +48,7 @@
 #include <linux/fsnotify.h>
 #include <linux/irq_work.h>
 #include <linux/workqueue.h>
+#include <linux/anon_inodes.h>
 
 #include "trace.h"
 #include "trace_output.h"
@@ -4290,7 +4291,7 @@ static int s_show(struct seq_file *m, void *v)
  */
 static inline int tracing_get_cpu(struct inode *inode)
 {
-	if (inode->i_cdev) /* See trace_create_cpu_file() */
+	if (inode && inode->i_cdev) /* See trace_create_cpu_file() */
 		return (long)inode->i_cdev - 1;
 	return RING_BUFFER_ALL_CPUS;
 }
@@ -6126,32 +6127,22 @@ tracing_max_lat_write(struct file *filp, const char __user *ubuf,
 
 #endif
 
-static int tracing_open_pipe(struct inode *inode, struct file *filp)
+static struct trace_iterator *
+tracing_create_pipe_iter(struct trace_array *tr, struct inode *inode)
 {
-	struct trace_array *tr = inode->i_private;
 	struct trace_iterator *iter;
-	int ret;
-
-	ret = tracing_check_open_get_tr(tr);
-	if (ret)
-		return ret;
-
-	mutex_lock(&trace_types_lock);
 
 	/* create a buffer to store the information to pass to userspace */
 	iter = kzalloc(sizeof(*iter), GFP_KERNEL);
-	if (!iter) {
-		ret = -ENOMEM;
-		__trace_array_put(tr);
-		goto out;
-	}
+	if (!iter)
+		return ERR_PTR(-ENOMEM);
 
 	trace_seq_init(&iter->seq);
 	iter->trace = tr->current_trace;
 
 	if (!alloc_cpumask_var(&iter->started, GFP_KERNEL)) {
-		ret = -ENOMEM;
-		goto fail;
+		kfree(iter);
+		return ERR_PTR(-ENOMEM);
 	}
 
 	/* trace pipe does not show start of buffer */
@@ -6168,6 +6159,29 @@ static int tracing_open_pipe(struct inode *inode, struct file *filp)
 	iter->array_buffer = &tr->array_buffer;
 	iter->cpu_file = tracing_get_cpu(inode);
 	mutex_init(&iter->mutex);
+
+	return iter;
+}
+
+static int tracing_open_pipe(struct inode *inode, struct file *filp)
+{
+	struct trace_array *tr = inode->i_private;
+	struct trace_iterator *iter;
+	int ret;
+
+	ret = tracing_check_open_get_tr(tr);
+	if (ret)
+		return ret;
+
+	mutex_lock(&trace_types_lock);
+
+	iter = tracing_create_pipe_iter(tr, inode);
+	if (IS_ERR(iter)) {
+		ret = PTR_ERR(iter);
+		__trace_array_put(tr);
+		goto out;
+	}
+
 	filp->private_data = iter;
 
 	if (iter->trace->pipe_open)
@@ -6179,18 +6193,12 @@ static int tracing_open_pipe(struct inode *inode, struct file *filp)
 out:
 	mutex_unlock(&trace_types_lock);
 	return ret;
-
-fail:
-	kfree(iter);
-	__trace_array_put(tr);
-	mutex_unlock(&trace_types_lock);
-	return ret;
 }
 
 static int tracing_release_pipe(struct inode *inode, struct file *file)
 {
 	struct trace_iterator *iter = file->private_data;
-	struct trace_array *tr = inode->i_private;
+	struct trace_array *tr = iter->tr;
 
 	mutex_lock(&trace_types_lock);
 
@@ -8048,7 +8056,7 @@ static inline __init int register_snapshot_cmd(void) { return 0; }
 
 static struct dentry *tracing_get_dentry(struct trace_array *tr)
 {
-	if (WARN_ON(!tr->dir))
+	if (!tr->dir)
 		return ERR_PTR(-ENODEV);
 
 	/* Top directory uses NULL as the parent */
@@ -8688,7 +8696,7 @@ static int trace_array_create_dir(struct trace_array *tr)
 	return ret;
 }
 
-static struct trace_array *trace_array_create(const char *name)
+static struct trace_array *__trace_array_create(const char *name)
 {
 	struct trace_array *tr;
 	int ret;
@@ -8698,9 +8706,11 @@ static struct trace_array *trace_array_create(const char *name)
 	if (!tr)
 		return ERR_PTR(ret);
 
-	tr->name = kstrdup(name, GFP_KERNEL);
-	if (!tr->name)
-		goto out_free_tr;
+	if (name) {
+		tr->name = kstrdup(name, GFP_KERNEL);
+		if (!tr->name)
+			goto out_free_tr;
+	}
 
 	if (!alloc_cpumask_var(&tr->tracing_cpumask, GFP_KERNEL))
 		goto out_free_tr;
@@ -8731,9 +8741,11 @@ static struct trace_array *trace_array_create(const char *name)
 	init_trace_flags_index(tr);
 
 	if (trace_instance_dir) {
-		ret = trace_array_create_dir(tr);
-		if (ret)
-			goto out_free_tr;
+		if (name) {
+			ret = trace_array_create_dir(tr);
+			if (ret)
+				goto out_free_tr;
+		}
 	} else
 		__trace_early_add_events(tr);
 
@@ -8753,6 +8765,18 @@ static struct trace_array *trace_array_create(const char *name)
 	return ERR_PTR(ret);
 }
 
+/**
+ * trace_array_create - Create a new anonymous trace array.
+ *
+ * Returns pointer to a new unamed trace array.
+ * NULL, if cannot be created.
+ */
+struct trace_array *trace_array_create(void)
+{
+	return __trace_array_create(NULL);
+}
+EXPORT_SYMBOL_GPL(trace_array_create);
+
 static int instance_mkdir(const char *name)
 {
 	struct trace_array *tr;
@@ -8765,7 +8789,7 @@ static int instance_mkdir(const char *name)
 	if (trace_array_find(name))
 		goto out_unlock;
 
-	tr = trace_array_create(name);
+	tr = __trace_array_create(name);
 
 	ret = PTR_ERR_OR_ZERO(tr);
 
@@ -8803,7 +8827,7 @@ struct trace_array *trace_array_get_by_name(const char *name)
 			goto out_unlock;
 	}
 
-	tr = trace_array_create(name);
+	tr = __trace_array_create(name);
 
 	if (IS_ERR(tr))
 		tr = NULL;
@@ -8838,7 +8862,8 @@ static int __remove_instance(struct trace_array *tr)
 	event_trace_del_tracer(tr);
 	ftrace_clear_pids(tr);
 	ftrace_destroy_function_files(tr);
-	tracefs_remove(tr->dir);
+	if (tr->dir)
+		tracefs_remove(tr->dir);
 	free_trace_buffers(tr);
 
 	for (i = 0; i < tr->nr_topts; i++) {
@@ -9427,6 +9452,53 @@ void ftrace_dump(enum ftrace_dump_mode oops_dump_mode)
 }
 EXPORT_SYMBOL_GPL(ftrace_dump);
 
+/**
+ * anon_trace_getfd - wrap the given trace array in an anonymous fd
+ *
+ * This creates a new anonymous fd that can be returned to userspace
+ * where it acts as a pipe giving read access to the trace buffer.
+ */
+int anon_trace_getfd(const char *name, struct trace_array *tr)
+{
+	struct trace_iterator *iter;
+	int ret;
+
+	if (!tr || trace_array_get(tr) < 0)
+		return -ENODEV;
+
+	mutex_lock(&trace_types_lock);
+
+	iter = tracing_create_pipe_iter(tr, NULL);
+	if (IS_ERR(iter)) {
+		ret = PTR_ERR(iter);
+		__trace_array_put(tr);
+		goto out;
+	}
+
+	ret = anon_inode_getfd(name, &tracing_pipe_fops, iter, O_CLOEXEC);
+	if (ret < 0)
+		goto fail;
+
+	if (iter->trace->pipe_open)
+		iter->trace->pipe_open(iter);
+
+	tr->ref++;
+out:
+	mutex_unlock(&trace_types_lock);
+	return ret;
+
+fail:
+	mutex_unlock(&trace_types_lock);
+
+	free_cpumask_var(iter->started);
+	mutex_destroy(&iter->mutex);
+	kfree(iter);
+
+	trace_array_put(tr);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(anon_trace_getfd);
+
 int trace_run_command(const char *buf, int (*createfn)(int, char **))
 {
 	char **argv;
-- 
2.20.1



More information about the Intel-gfx-trybot mailing list