From nobody Thu Feb 12 02:02:12 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 4F3C4EE0211 for ; Wed, 13 Sep 2023 23:28:48 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S233156AbjIMX2v (ORCPT ); Wed, 13 Sep 2023 19:28:51 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:44858 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231515AbjIMX2u (ORCPT ); Wed, 13 Sep 2023 19:28:50 -0400 Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 2F6CA1BCB for ; Wed, 13 Sep 2023 16:28:46 -0700 (PDT) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 0697EC433C7; Wed, 13 Sep 2023 23:28:44 +0000 (UTC) Date: Wed, 13 Sep 2023 19:29:05 -0400 From: Steven Rostedt To: Linus Torvalds Cc: LKML , Masami Hiramatsu , Mark Rutland , Ajay Kaher Subject: [GIT PULL] tracing: Add eventfs file to help with debugging any more issues Message-ID: <20230913192905.0a92bcab@gandalf.local.home> X-Mailer: Claws Mail 3.19.1 (GTK+ 2.24.33; x86_64-pc-linux-gnu) MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" Linus, tracing: Add eventfs file to help with debugging any more issues While debugging the eventfs dynamic file creation issues, creating a file in tracefs that exposed what dentries that were created along with their ref counts proved invaluable. Please pull the latest trace-v6.6-rc1-2 tree, which can be found at: git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace.git trace-v6.6-rc1-2 Tag SHA1: 48380652486eacb149420e083516fb159b475d13 Head SHA1: d6f358d6fff619cebbc547f2bdd3ec66ade04954 Steven Rostedt (Google) (1): tracefs: Add show_events_dentries ---- fs/tracefs/Makefile | 1 + fs/tracefs/event_inode.c | 42 +------------ fs/tracefs/event_show.c | 147 ++++++++++++++++++++++++++++++++++++++++= ++++ fs/tracefs/internal.h | 44 +++++++++++++ include/linux/tracefs.h | 2 + kernel/trace/trace_events.c | 3 + 6 files changed, 198 insertions(+), 41 deletions(-) create mode 100644 fs/tracefs/event_show.c --------------------------- commit d6f358d6fff619cebbc547f2bdd3ec66ade04954 Author: Steven Rostedt (Google) Date: Wed Sep 13 11:36:28 2023 -0400 tracefs: Add show_events_dentries =20 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 h= ave been dynamically allocated as well as their current ref counts. =20 ~# 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 =20 The first value is the name of the file or directory. If a dentry is allocated, then a "dentry: ()" is displayed showing the addr= ess of the dentry as well as its ref count. =20 Link: https://lore.kernel.org/linux-trace-kernel/20230913113628.172907b= 7@gandalf.local.home =20 Cc: Masami Hiramatsu Cc: Mark Rutland Cc: Ajay Kaher Signed-off-by: Steven Rostedt (Google) diff --git a/fs/tracefs/Makefile b/fs/tracefs/Makefile index 73c56da8e284..8f48f4fc6698 100644 --- a/fs/tracefs/Makefile +++ b/fs/tracefs/Makefile @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only tracefs-objs :=3D inode.o tracefs-objs +=3D event_inode.o +tracefs-objs +=3D event_show.o =20 obj-$(CONFIG_TRACING) +=3D tracefs.o =20 diff --git a/fs/tracefs/event_inode.c b/fs/tracefs/event_inode.c index 9f64e7332796..b23bb0957bb4 100644 --- a/fs/tracefs/event_inode.c +++ b/fs/tracefs/event_inode.c @@ -23,47 +23,7 @@ #include #include "internal.h" =20 -struct eventfs_inode { - struct list_head e_top_files; -}; - -/* - * struct eventfs_file - hold the properties of the eventfs files and - * directories. - * @name: the name of the file or directory to create - * @d_parent: holds parent's dentry - * @dentry: once accessed holds dentry - * @list: file or directory to be added to parent directory - * @ei: list of files and directories within directory - * @fop: file_operations for file or directory - * @iop: inode_operations for file or directory - * @data: something that the caller will want to get to later on - * @mode: the permission that the file or directory should have - */ -struct eventfs_file { - const char *name; - struct dentry *d_parent; - struct dentry *dentry; - struct list_head list; - struct eventfs_inode *ei; - const struct file_operations *fop; - const struct inode_operations *iop; - /* - * Union - used for deletion - * @del_list: list of eventfs_file to delete - * @rcu: eventfs_file to delete in RCU - * @is_freed: node is freed if one of the above is set - */ - union { - struct list_head del_list; - struct rcu_head rcu; - unsigned long is_freed; - }; - void *data; - umode_t mode; -}; - -static DEFINE_MUTEX(eventfs_mutex); +DEFINE_MUTEX(eventfs_mutex); DEFINE_STATIC_SRCU(eventfs_srcu); =20 static struct dentry *eventfs_root_lookup(struct inode *dir, diff --git a/fs/tracefs/event_show.c b/fs/tracefs/event_show.c new file mode 100644 index 000000000000..66dece7cc810 --- /dev/null +++ b/fs/tracefs/event_show.c @@ -0,0 +1,147 @@ +#include +#include +#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/ + * level 2 : /sys/kernel/tracing/events//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 =3D m->private; + int level =3D elist->level; + struct list_head *head =3D elist->head[level]; + struct list_head *next =3D elist->next[level]; + struct eventfs_file *ef; + + (*pos)++; + + /* If next is equal to head, then the list is complete */ + while (next =3D=3D head) { + if (!level) + return NULL; + + /* sublevel below top level, go up one */ + elist->level =3D --level; + head =3D elist->head[level]; + /* Going down does not update next, so do it here */ + next =3D elist->next[level]->next; + elist->next[level] =3D next; + } + + ef =3D list_entry(next, struct eventfs_file, list); + + /* For each entry (not at the bottom) do a breadth first search */ + if (ef->ei && !list_empty(&ef->ei->e_top_files) && level < 2) { + elist->level =3D ++level; + head =3D &ef->ei->e_top_files; + elist->head[level] =3D head; + next =3D 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] =3D next->next; + return ef; +} + +static void *e_start(struct seq_file *m, loff_t *pos) +{ + struct event_list *elist =3D m->private; + struct eventfs_file *ef =3D NULL; + loff_t l; + + mutex_lock(&eventfs_mutex); + + elist->level =3D 0; + elist->next[0] =3D elist->head[0]->next; + + for (l =3D 0; l <=3D *pos; ) { + ef =3D e_next(m, ef, &l); + if (!ef) + break; + } + return ef; +} + +static int e_show(struct seq_file *m, void *v) +{ + struct eventfs_file *ef =3D v; + + seq_printf(m, "%s", ef->name); + if (ef->ei) + seq_putc(m, '/'); + + if (ef->dentry) + seq_printf(m, " dentry: (%d)", d_count(ef->dentry)); + seq_putc(m, '\n'); + + 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 =3D { + .start =3D e_start, + .next =3D e_next, + .show =3D e_show, + .stop =3D e_stop, +}; + +static int +eventfs_show_dentry_open(struct inode *inode, struct file *file) +{ + const struct seq_operations *seq_ops =3D &eventfs_show_dentry_seq_ops; + struct event_list *elist; + struct tracefs_inode *ti; + struct eventfs_inode *ei; + struct dentry *dentry; + + /* The inode private should have the dentry of the "events" directory */ + dentry =3D inode->i_private; + if (!dentry) + return -EINVAL; + + elist =3D __seq_open_private(file, seq_ops, sizeof(*elist)); + if (!elist) + return -ENOMEM; + + ti =3D get_tracefs(dentry->d_inode); + ei =3D ti->private; + + /* + * Start off at level 0 (/sys/kernel/tracing/events) + * Initialize head to the events files and next to the + * first file. + */ + elist->level =3D 0; + elist->head[0] =3D &ei->e_top_files; + elist->next[0] =3D ei->e_top_files.next; + + return 0; +} + +const struct file_operations eventfs_show_dentry_fops =3D { + .open =3D eventfs_show_dentry_open, + .read =3D seq_read, + .llseek =3D seq_lseek, + .release =3D seq_release, +}; diff --git a/fs/tracefs/internal.h b/fs/tracefs/internal.h index 4f2e49e2197b..461920f0133f 100644 --- a/fs/tracefs/internal.h +++ b/fs/tracefs/internal.h @@ -2,11 +2,55 @@ #ifndef _TRACEFS_INTERNAL_H #define _TRACEFS_INTERNAL_H =20 +#include + enum { TRACEFS_EVENT_INODE =3D BIT(1), TRACEFS_EVENT_TOP_INODE =3D BIT(2), }; =20 +struct eventfs_inode { + struct list_head e_top_files; +}; + +/* + * struct eventfs_file - hold the properties of the eventfs files and + * directories. + * @name: the name of the file or directory to create + * @d_parent: holds parent's dentry + * @dentry: once accessed holds dentry + * @list: file or directory to be added to parent directory + * @ei: list of files and directories within directory + * @fop: file_operations for file or directory + * @iop: inode_operations for file or directory + * @data: something that the caller will want to get to later on + * @mode: the permission that the file or directory should have + */ +struct eventfs_file { + const char *name; + struct dentry *d_parent; + struct dentry *dentry; + struct list_head list; + struct eventfs_inode *ei; + const struct file_operations *fop; + const struct inode_operations *iop; + /* + * Union - used for deletion + * @del_list: list of eventfs_file to delete + * @rcu: eventfs_file to delete in RCU + * @is_freed: node is freed if one of the above is set + */ + union { + struct list_head del_list; + struct rcu_head rcu; + unsigned long is_freed; + }; + void *data; + umode_t mode; +}; + +extern struct mutex eventfs_mutex; + struct tracefs_inode { unsigned long flags; void *private; diff --git a/include/linux/tracefs.h b/include/linux/tracefs.h index 009072792fa3..f76c7d74b23d 100644 --- a/include/linux/tracefs.h +++ b/include/linux/tracefs.h @@ -58,6 +58,8 @@ struct dentry *tracefs_create_instance_dir(const char *na= me, struct dentry *pare =20 bool tracefs_initialized(void); =20 +extern const struct file_operations eventfs_show_dentry_fops; + #endif /* CONFIG_TRACING */ =20 #endif diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c index 91951d038ba4..5b0cc40910b2 100644 --- a/kernel/trace/trace_events.c +++ b/kernel/trace/trace_events.c @@ -3639,6 +3639,9 @@ create_event_toplevel_files(struct dentry *parent, st= ruct trace_array *tr) return -ENOMEM; } =20 + trace_create_file("show_events_dentries", TRACE_MODE_READ, parent, d_even= ts, + &eventfs_show_dentry_fops); + error =3D eventfs_add_events_file("enable", TRACE_MODE_WRITE, d_events, tr, &ftrace_tr_enable_fops); if (error)