aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSteven Rostedt (Google) <rostedt@goodmis.org>2023-09-13 11:36:28 -0400
committerSteven Rostedt (Google) <rostedt@goodmis.org>2024-01-07 13:23:29 -0500
commit61f3afbc042abbcdba7e5b945d8ef71601852d2a (patch)
tree0237ea48b63511fa3825b288c6f0d298eeb36c7c
parent1de94b52d5e8d8b32f0252f14fad1f1edc2e71f1 (diff)
downloadlinux-trace-eventfs-show-files.tar.gz
tracefs: Add show_events_dentrieseventfs-show-files
Add a file in tracefs that shows the "events" allocated entries and the dentries that are attached to them. This is used to see what dentries have been dynamically allocated as well as their current ref counts. ~# cat /sys/kernel/tracing/events/sched/sched_switch/enable 0 ~# grep -A4 sched_switch /sys/kernel/tracing/show_events_dentries sched_switch/ dentry: (1) enable dentry: (0) id filter trigger The first value is the name of the file or directory. If a dentry is allocated, then a "dentry: (<ref-count>)" is displayed showing the address of the dentry as well as its ref count. Link: https://lore.kernel.org/linux-trace-kernel/20230913113628.172907b7@gandalf.local.home Cc: Masami Hiramatsu <mhiramat@kernel.org> Cc: Mark Rutland <mark.rutland@arm.com> Cc: Ajay Kaher <akaher@vmware.com> Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org>
-rw-r--r--fs/tracefs/Makefile1
-rw-r--r--fs/tracefs/event_inode.c2
-rw-r--r--fs/tracefs/event_show.c156
-rw-r--r--fs/tracefs/internal.h4
-rw-r--r--include/linux/tracefs.h2
-rw-r--r--kernel/trace/trace_events.c3
6 files changed, 167 insertions, 1 deletions
diff --git a/fs/tracefs/Makefile b/fs/tracefs/Makefile
index 73c56da8e284e..8f48f4fc66983 100644
--- a/fs/tracefs/Makefile
+++ b/fs/tracefs/Makefile
@@ -1,6 +1,7 @@
# SPDX-License-Identifier: GPL-2.0-only
tracefs-objs := inode.o
tracefs-objs += event_inode.o
+tracefs-objs += event_show.o
obj-$(CONFIG_TRACING) += tracefs.o
diff --git a/fs/tracefs/event_inode.c b/fs/tracefs/event_inode.c
index fdff53d5a1f87..b698ead92f278 100644
--- a/fs/tracefs/event_inode.c
+++ b/fs/tracefs/event_inode.c
@@ -30,7 +30,7 @@
* if ei->is_freed is not set. When ei->is_freed is set, the dentry
* is on its way to being freed after the last dput() is made on it.
*/
-static DEFINE_MUTEX(eventfs_mutex);
+DEFINE_MUTEX(eventfs_mutex);
/*
* The eventfs_inode (ei) itself is protected by SRCU. It is released from
diff --git a/fs/tracefs/event_show.c b/fs/tracefs/event_show.c
new file mode 100644
index 0000000000000..c319af74e0d7b
--- /dev/null
+++ b/fs/tracefs/event_show.c
@@ -0,0 +1,156 @@
+#include <linux/seq_file.h>
+#include <linux/tracefs.h>
+#include "internal.h"
+
+/*
+ * This will iterate three lists that correspond to the directory level
+ * of the eventfs directory.
+ *
+ * level 0 : /sys/kernel/tracing/events
+ * level 1 : /sys/kernel/tracing/events/<system>
+ * level 2 : /sys/kernel/tracing/events/<system>/event
+ *
+ * The iterator needs to see all levels as they all contain dynamically
+ * allocated dentries and inodes.
+ */
+struct event_list {
+ int level;
+ struct list_head *head[3];
+ struct list_head *next[3];
+};
+
+static void *e_next(struct seq_file *m, void *v, loff_t *pos)
+{
+ struct event_list *elist = m->private;
+ int level = elist->level;
+ struct list_head *head = elist->head[level];
+ struct list_head *next = elist->next[level];
+ struct eventfs_inode *ei;
+
+ (*pos)++;
+
+ /* If next is equal to head, then the list is complete */
+ while (next == head) {
+ if (!level)
+ return NULL;
+
+ /* sublevel below top level, go up one */
+ elist->level = --level;
+ head = elist->head[level];
+ /* Going down does not update next, so do it here */
+ next = elist->next[level]->next;
+ elist->next[level] = next;
+ }
+
+ if (!next)
+ return NULL;
+
+ ei = list_entry(next, struct eventfs_inode, list);
+
+ /* For each entry (not at the bottom) do a breadth first search */
+ if (!list_empty(&ei->children) && level < 2) {
+ elist->level = ++level;
+ head = &ei->children;
+ elist->head[level] = head;
+ next = head;
+ /*
+ * Note, next is now pointing to the next sub level.
+ * Need to update the next for the previous level on the way up.
+ */
+ }
+
+ elist->next[level] = next->next;
+ return ei;
+}
+
+static void *e_start(struct seq_file *m, loff_t *pos)
+{
+ struct event_list *elist = m->private;
+ struct eventfs_inode *ei = NULL;
+ loff_t l;
+
+ mutex_lock(&eventfs_mutex);
+
+ elist->level = 0;
+ elist->next[0] = elist->head[0]->next;
+
+ if (!*pos) {
+ (*pos)++;
+ return container_of(elist->head[0], struct eventfs_inode, children);
+ }
+
+ for (l = 1; l <= *pos; ) {
+ ei = e_next(m, ei, &l);
+ if (!ei)
+ break;
+ }
+ return ei;
+}
+
+static int e_show(struct seq_file *m, void *v)
+{
+ struct eventfs_inode *ei = v;
+ int i;
+
+ seq_printf(m, "%s", ei->name);
+
+ if (ei->dentry)
+ seq_printf(m, " dentry: (%d)", d_count(ei->dentry));
+ seq_putc(m, '\n');
+
+ for (i = 0; i < ei->nr_entries; i++) {
+ struct dentry *dentry = ei->d_children[i];
+ if (dentry) {
+ seq_printf(m, " %s dentry: %px (%d)\n",
+ ei->entries[i].name, dentry, d_count(dentry));
+ }
+ }
+ return 0;
+}
+
+static void e_stop(struct seq_file *m, void *p)
+{
+ mutex_unlock(&eventfs_mutex);
+}
+
+static const struct seq_operations eventfs_show_dentry_seq_ops = {
+ .start = e_start,
+ .next = e_next,
+ .show = e_show,
+ .stop = e_stop,
+};
+
+static int
+eventfs_show_dentry_open(struct inode *inode, struct file *file)
+{
+ const struct seq_operations *seq_ops = &eventfs_show_dentry_seq_ops;
+ struct event_list *elist;
+ struct eventfs_inode *ei;
+
+ /* The inode private should have the eventfs_inode of the "events" directory */
+ ei = inode->i_private;
+ if (!ei)
+ return -EINVAL;
+
+ elist = __seq_open_private(file, seq_ops, sizeof(*elist));
+ if (!elist)
+ return -ENOMEM;
+
+ /*
+ * Start off at level 0 (/sys/kernel/tracing/events)
+ * Initialize head to the events files and next to the
+ * first file.
+ */
+ elist->level = 0;
+ elist->head[0] = &ei->children;
+ elist->next[0] = ei->children.next;
+
+ return 0;
+}
+
+const struct file_operations eventfs_show_dentry_fops = {
+ .open = eventfs_show_dentry_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release_private,
+};
diff --git a/fs/tracefs/internal.h b/fs/tracefs/internal.h
index 12b7d0150ae9e..a00c16cef82ba 100644
--- a/fs/tracefs/internal.h
+++ b/fs/tracefs/internal.h
@@ -2,6 +2,8 @@
#ifndef _TRACEFS_INTERNAL_H
#define _TRACEFS_INTERNAL_H
+#include <linux/mutex.h>
+
enum {
TRACEFS_EVENT_INODE = BIT(1),
TRACEFS_EVENT_TOP_INODE = BIT(2),
@@ -74,6 +76,8 @@ static inline struct tracefs_inode *get_tracefs(const struct inode *inode)
return container_of(inode, struct tracefs_inode, vfs_inode);
}
+extern struct mutex eventfs_mutex;
+
struct dentry *tracefs_start_creating(const char *name, struct dentry *parent);
struct dentry *tracefs_end_creating(struct dentry *dentry);
struct dentry *tracefs_failed_creating(struct dentry *dentry);
diff --git a/include/linux/tracefs.h b/include/linux/tracefs.h
index 7a5fe17b6bf9c..dad748eaaadf9 100644
--- a/include/linux/tracefs.h
+++ b/include/linux/tracefs.h
@@ -101,6 +101,8 @@ struct dentry *tracefs_create_instance_dir(const char *name, struct dentry *pare
bool tracefs_initialized(void);
+extern const struct file_operations eventfs_show_dentry_fops;
+
#endif /* CONFIG_TRACING */
#endif
diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c
index f29e815ca5b2e..5d9335931345d 100644
--- a/kernel/trace/trace_events.c
+++ b/kernel/trace/trace_events.c
@@ -3802,6 +3802,9 @@ create_event_toplevel_files(struct dentry *parent, struct trace_array *tr)
return -ENOMEM;
}
+ trace_create_file("show_events_dentries", TRACE_MODE_READ, parent, e_events,
+ &eventfs_show_dentry_fops);
+
/* There are not as crucial, just warn if they are not created */
trace_create_file("set_event_pid", TRACE_MODE_WRITE, parent,