mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-11 21:38:32 +08:00
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/20231101172650.552471568@goodmis.org
Cc: stable@vger.kernel.org
Cc: Masami Hiramatsu <mhiramat@kernel.org>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Fixes: 5bdcd5f533
("eventfs: Implement removal of meta data from eventfs")
Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org>
This commit is contained in:
parent
62d65cac11
commit
407c6726ca
@ -967,30 +967,29 @@ static void unhook_dentry(struct dentry *dentry)
|
|||||||
{
|
{
|
||||||
if (!dentry)
|
if (!dentry)
|
||||||
return;
|
return;
|
||||||
|
/*
|
||||||
/* Keep the dentry from being freed yet (see eventfs_workfn()) */
|
* Need to add a reference to the dentry that is expected by
|
||||||
|
* simple_recursive_removal(), which will include a dput().
|
||||||
|
*/
|
||||||
dget(dentry);
|
dget(dentry);
|
||||||
|
|
||||||
dentry->d_fsdata = NULL;
|
/*
|
||||||
d_invalidate(dentry);
|
* Also add a reference for the dput() in eventfs_workfn().
|
||||||
mutex_lock(&eventfs_mutex);
|
* That is required as that dput() will free the ei after
|
||||||
/* dentry should now have at least a single reference */
|
* the SRCU grace period is over.
|
||||||
WARN_ONCE((int)d_count(dentry) < 1,
|
*/
|
||||||
"dentry %px (%s) less than one reference (%d) after invalidate\n",
|
dget(dentry);
|
||||||
dentry, dentry->d_name.name, d_count(dentry));
|
|
||||||
mutex_unlock(&eventfs_mutex);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* eventfs_remove_rec - remove eventfs dir or file from list
|
* eventfs_remove_rec - remove eventfs dir or file from list
|
||||||
* @ei: eventfs_inode to be removed.
|
* @ei: eventfs_inode to be removed.
|
||||||
* @head: the list head to place the deleted @ei and children
|
|
||||||
* @level: prevent recursion from going more than 3 levels deep.
|
* @level: prevent recursion from going more than 3 levels deep.
|
||||||
*
|
*
|
||||||
* This function recursively removes eventfs_inodes which
|
* This function recursively removes eventfs_inodes which
|
||||||
* contains info of files and/or directories.
|
* contains info of files and/or directories.
|
||||||
*/
|
*/
|
||||||
static void eventfs_remove_rec(struct eventfs_inode *ei, struct list_head *head, int level)
|
static void eventfs_remove_rec(struct eventfs_inode *ei, int level)
|
||||||
{
|
{
|
||||||
struct eventfs_inode *ei_child;
|
struct eventfs_inode *ei_child;
|
||||||
|
|
||||||
@ -1009,13 +1008,26 @@ static void eventfs_remove_rec(struct eventfs_inode *ei, struct list_head *head,
|
|||||||
/* search for nested folders or files */
|
/* search for nested folders or files */
|
||||||
list_for_each_entry_srcu(ei_child, &ei->children, list,
|
list_for_each_entry_srcu(ei_child, &ei->children, list,
|
||||||
lockdep_is_held(&eventfs_mutex)) {
|
lockdep_is_held(&eventfs_mutex)) {
|
||||||
eventfs_remove_rec(ei_child, head, level + 1);
|
/* Children only have dentry if parent does */
|
||||||
|
WARN_ON_ONCE(ei_child->dentry && !ei->dentry);
|
||||||
|
eventfs_remove_rec(ei_child, level + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
ei->is_freed = 1;
|
ei->is_freed = 1;
|
||||||
|
|
||||||
|
for (int i = 0; i < ei->nr_entries; i++) {
|
||||||
|
if (ei->d_children[i]) {
|
||||||
|
/* Children only have dentry if parent does */
|
||||||
|
WARN_ON_ONCE(!ei->dentry);
|
||||||
|
unhook_dentry(ei->d_children[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unhook_dentry(ei->dentry);
|
||||||
|
|
||||||
list_del_rcu(&ei->list);
|
list_del_rcu(&ei->list);
|
||||||
list_add_tail(&ei->del_list, head);
|
call_srcu(&eventfs_srcu, &ei->rcu, free_rcu_ei);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1026,30 +1038,22 @@ static void eventfs_remove_rec(struct eventfs_inode *ei, struct list_head *head,
|
|||||||
*/
|
*/
|
||||||
void eventfs_remove_dir(struct eventfs_inode *ei)
|
void eventfs_remove_dir(struct eventfs_inode *ei)
|
||||||
{
|
{
|
||||||
struct eventfs_inode *tmp;
|
struct dentry *dentry;
|
||||||
LIST_HEAD(ei_del_list);
|
|
||||||
|
|
||||||
if (!ei)
|
if (!ei)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/*
|
|
||||||
* Move the deleted eventfs_inodes onto the ei_del_list
|
|
||||||
* which will also set the is_freed value. Note, this has to be
|
|
||||||
* done under the eventfs_mutex, but the deletions of
|
|
||||||
* the dentries must be done outside the eventfs_mutex.
|
|
||||||
* Hence moving them to this temporary list.
|
|
||||||
*/
|
|
||||||
mutex_lock(&eventfs_mutex);
|
mutex_lock(&eventfs_mutex);
|
||||||
eventfs_remove_rec(ei, &ei_del_list, 0);
|
dentry = ei->dentry;
|
||||||
|
eventfs_remove_rec(ei, 0);
|
||||||
mutex_unlock(&eventfs_mutex);
|
mutex_unlock(&eventfs_mutex);
|
||||||
|
|
||||||
list_for_each_entry_safe(ei, tmp, &ei_del_list, del_list) {
|
/*
|
||||||
for (int i = 0; i < ei->nr_entries; i++)
|
* If any of the ei children has a dentry, then the ei itself
|
||||||
unhook_dentry(ei->d_children[i]);
|
* must have a dentry.
|
||||||
unhook_dentry(ei->dentry);
|
*/
|
||||||
list_del(&ei->del_list);
|
if (dentry)
|
||||||
call_srcu(&eventfs_srcu, &ei->rcu, free_rcu_ei);
|
simple_recursive_removal(dentry, NULL);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1060,10 +1064,17 @@ void eventfs_remove_dir(struct eventfs_inode *ei)
|
|||||||
*/
|
*/
|
||||||
void eventfs_remove_events_dir(struct eventfs_inode *ei)
|
void eventfs_remove_events_dir(struct eventfs_inode *ei)
|
||||||
{
|
{
|
||||||
struct dentry *dentry = ei->dentry;
|
struct dentry *dentry;
|
||||||
|
|
||||||
|
dentry = ei->dentry;
|
||||||
eventfs_remove_dir(ei);
|
eventfs_remove_dir(ei);
|
||||||
|
|
||||||
/* Matches the dget() from eventfs_create_events_dir() */
|
/*
|
||||||
|
* Matches the dget() done by tracefs_start_creating()
|
||||||
|
* in eventfs_create_events_dir() when it the dentry was
|
||||||
|
* created. In other words, it's a normal dentry that
|
||||||
|
* sticks around while the other ei->dentry are created
|
||||||
|
* and destroyed dynamically.
|
||||||
|
*/
|
||||||
dput(dentry);
|
dput(dentry);
|
||||||
}
|
}
|
||||||
|
@ -55,12 +55,10 @@ struct eventfs_inode {
|
|||||||
/*
|
/*
|
||||||
* Union - used for deletion
|
* Union - used for deletion
|
||||||
* @llist: for calling dput() if needed after RCU
|
* @llist: for calling dput() if needed after RCU
|
||||||
* @del_list: list of eventfs_inode to delete
|
|
||||||
* @rcu: eventfs_inode to delete in RCU
|
* @rcu: eventfs_inode to delete in RCU
|
||||||
*/
|
*/
|
||||||
union {
|
union {
|
||||||
struct llist_node llist;
|
struct llist_node llist;
|
||||||
struct list_head del_list;
|
|
||||||
struct rcu_head rcu;
|
struct rcu_head rcu;
|
||||||
};
|
};
|
||||||
unsigned int is_freed:1;
|
unsigned int is_freed:1;
|
||||||
|
Loading…
Reference in New Issue
Block a user