Skip to content

Commit 407c672

Browse files
committed
eventfs: Use simple_recursive_removal() to clean up dentries
Looking at how dentry is removed via the tracefs system, I found that eventfs does not do everything that it did under tracefs. The tracefs removal of a dentry calls simple_recursive_removal() that does a lot more than a simple d_invalidate(). As it should be a requirement that any eventfs_inode that has a dentry, so does its parent. When removing a eventfs_inode, if it has a dentry, a call to simple_recursive_removal() on that dentry should clean up all the dentries underneath it. Add WARN_ON_ONCE() to check for the parent having a dentry if any children do. Link: https://lore.kernel.org/all/20231101022553.GE1957730@ZenIV/ Link: https://lkml.kernel.org/r/[email protected] Cc: [email protected] Cc: Masami Hiramatsu <[email protected]> Cc: Mark Rutland <[email protected]> Cc: Andrew Morton <[email protected]> Cc: Al Viro <[email protected]> Fixes: 5bdcd5f ("eventfs: Implement removal of meta data from eventfs") Signed-off-by: Steven Rostedt (Google) <[email protected]>
1 parent 62d65ca commit 407c672

File tree

2 files changed

+44
-35
lines changed

2 files changed

+44
-35
lines changed

fs/tracefs/event_inode.c

Lines changed: 44 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -967,30 +967,29 @@ static void unhook_dentry(struct dentry *dentry)
967967
{
968968
if (!dentry)
969969
return;
970-
971-
/* Keep the dentry from being freed yet (see eventfs_workfn()) */
970+
/*
971+
* Need to add a reference to the dentry that is expected by
972+
* simple_recursive_removal(), which will include a dput().
973+
*/
972974
dget(dentry);
973975

974-
dentry->d_fsdata = NULL;
975-
d_invalidate(dentry);
976-
mutex_lock(&eventfs_mutex);
977-
/* dentry should now have at least a single reference */
978-
WARN_ONCE((int)d_count(dentry) < 1,
979-
"dentry %px (%s) less than one reference (%d) after invalidate\n",
980-
dentry, dentry->d_name.name, d_count(dentry));
981-
mutex_unlock(&eventfs_mutex);
976+
/*
977+
* Also add a reference for the dput() in eventfs_workfn().
978+
* That is required as that dput() will free the ei after
979+
* the SRCU grace period is over.
980+
*/
981+
dget(dentry);
982982
}
983983

984984
/**
985985
* eventfs_remove_rec - remove eventfs dir or file from list
986986
* @ei: eventfs_inode to be removed.
987-
* @head: the list head to place the deleted @ei and children
988987
* @level: prevent recursion from going more than 3 levels deep.
989988
*
990989
* This function recursively removes eventfs_inodes which
991990
* contains info of files and/or directories.
992991
*/
993-
static void eventfs_remove_rec(struct eventfs_inode *ei, struct list_head *head, int level)
992+
static void eventfs_remove_rec(struct eventfs_inode *ei, int level)
994993
{
995994
struct eventfs_inode *ei_child;
996995

@@ -1009,13 +1008,26 @@ static void eventfs_remove_rec(struct eventfs_inode *ei, struct list_head *head,
10091008
/* search for nested folders or files */
10101009
list_for_each_entry_srcu(ei_child, &ei->children, list,
10111010
lockdep_is_held(&eventfs_mutex)) {
1012-
eventfs_remove_rec(ei_child, head, level + 1);
1011+
/* Children only have dentry if parent does */
1012+
WARN_ON_ONCE(ei_child->dentry && !ei->dentry);
1013+
eventfs_remove_rec(ei_child, level + 1);
10131014
}
10141015

1016+
10151017
ei->is_freed = 1;
10161018

1019+
for (int i = 0; i < ei->nr_entries; i++) {
1020+
if (ei->d_children[i]) {
1021+
/* Children only have dentry if parent does */
1022+
WARN_ON_ONCE(!ei->dentry);
1023+
unhook_dentry(ei->d_children[i]);
1024+
}
1025+
}
1026+
1027+
unhook_dentry(ei->dentry);
1028+
10171029
list_del_rcu(&ei->list);
1018-
list_add_tail(&ei->del_list, head);
1030+
call_srcu(&eventfs_srcu, &ei->rcu, free_rcu_ei);
10191031
}
10201032

10211033
/**
@@ -1026,30 +1038,22 @@ static void eventfs_remove_rec(struct eventfs_inode *ei, struct list_head *head,
10261038
*/
10271039
void eventfs_remove_dir(struct eventfs_inode *ei)
10281040
{
1029-
struct eventfs_inode *tmp;
1030-
LIST_HEAD(ei_del_list);
1041+
struct dentry *dentry;
10311042

10321043
if (!ei)
10331044
return;
10341045

1035-
/*
1036-
* Move the deleted eventfs_inodes onto the ei_del_list
1037-
* which will also set the is_freed value. Note, this has to be
1038-
* done under the eventfs_mutex, but the deletions of
1039-
* the dentries must be done outside the eventfs_mutex.
1040-
* Hence moving them to this temporary list.
1041-
*/
10421046
mutex_lock(&eventfs_mutex);
1043-
eventfs_remove_rec(ei, &ei_del_list, 0);
1047+
dentry = ei->dentry;
1048+
eventfs_remove_rec(ei, 0);
10441049
mutex_unlock(&eventfs_mutex);
10451050

1046-
list_for_each_entry_safe(ei, tmp, &ei_del_list, del_list) {
1047-
for (int i = 0; i < ei->nr_entries; i++)
1048-
unhook_dentry(ei->d_children[i]);
1049-
unhook_dentry(ei->dentry);
1050-
list_del(&ei->del_list);
1051-
call_srcu(&eventfs_srcu, &ei->rcu, free_rcu_ei);
1052-
}
1051+
/*
1052+
* If any of the ei children has a dentry, then the ei itself
1053+
* must have a dentry.
1054+
*/
1055+
if (dentry)
1056+
simple_recursive_removal(dentry, NULL);
10531057
}
10541058

10551059
/**
@@ -1060,10 +1064,17 @@ void eventfs_remove_dir(struct eventfs_inode *ei)
10601064
*/
10611065
void eventfs_remove_events_dir(struct eventfs_inode *ei)
10621066
{
1063-
struct dentry *dentry = ei->dentry;
1067+
struct dentry *dentry;
10641068

1069+
dentry = ei->dentry;
10651070
eventfs_remove_dir(ei);
10661071

1067-
/* Matches the dget() from eventfs_create_events_dir() */
1072+
/*
1073+
* Matches the dget() done by tracefs_start_creating()
1074+
* in eventfs_create_events_dir() when it the dentry was
1075+
* created. In other words, it's a normal dentry that
1076+
* sticks around while the other ei->dentry are created
1077+
* and destroyed dynamically.
1078+
*/
10681079
dput(dentry);
10691080
}

fs/tracefs/internal.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,12 +55,10 @@ struct eventfs_inode {
5555
/*
5656
* Union - used for deletion
5757
* @llist: for calling dput() if needed after RCU
58-
* @del_list: list of eventfs_inode to delete
5958
* @rcu: eventfs_inode to delete in RCU
6059
*/
6160
union {
6261
struct llist_node llist;
63-
struct list_head del_list;
6462
struct rcu_head rcu;
6563
};
6664
unsigned int is_freed:1;

0 commit comments

Comments
 (0)