[PATCH v4 3/5] fsnotify: don't hold i_lock while walking d_children in fsnotify_set_children_dentry_flags()

From: NeilBrown

Date: Sun Oct 18 2026 - 20:10:41 EST


From: NeilBrown <neil@xxxxxxxxxx>

Rather than hold i_lock across a d_children walk, dget() a reference to
the dentry and drop the i_lock before the walk. This is easily done
with d_find_alias().

This requires that we dput() which can sleep. Now that no locks are
held across calls to fsnotify_set_children_dentry_flags(), this is safe.

Signed-off-by: NeilBrown <neil@xxxxxxxxxx>
---
fs/notify/fsnotify.c | 40 ++++++++++++++++++++--------------------
1 file changed, 20 insertions(+), 20 deletions(-)

diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c
index 7e2f330fd283..465e875dec57 100644
--- a/fs/notify/fsnotify.c
+++ b/fs/notify/fsnotify.c
@@ -71,32 +71,32 @@ void fsnotify_sb_free(struct super_block *sb)
*/
void fsnotify_set_children_dentry_flags(struct inode *inode)
{
- struct dentry *alias;
+ struct dentry *alias, *child;

if (!S_ISDIR(inode->i_mode))
return;

- spin_lock(&inode->i_lock);
- /* run all of the dentries associated with this inode. Since this is a
- * directory, there damn well better only be one item on this list */
- for_each_alias(alias, inode) {
- struct dentry *child;
-
- /* run all of the children of the original inode and fix their
- * d_flags to indicate parental interest (their parent is the
- * original inode) */
- spin_lock(&alias->d_lock);
- hlist_for_each_entry(child, &alias->d_children, d_sib) {
- if (!child->d_inode)
- continue;
+ /* Find the dentry for inode - there can only be one */
+ alias = d_find_alias(inode);
+ if (!alias)
+ return;

- spin_lock_nested(&child->d_lock, DENTRY_D_LOCK_NESTED);
- child->d_flags |= DCACHE_FSNOTIFY_PARENT_WATCHED;
- spin_unlock(&child->d_lock);
- }
- spin_unlock(&alias->d_lock);
+ /*
+ * run all of the children of the original inode and fix their
+ * d_flags to indicate parental interest (their parent is the
+ * original inode)
+ */
+ spin_lock(&alias->d_lock);
+ hlist_for_each_entry(child, &alias->d_children, d_sib) {
+ if (!child->d_inode)
+ continue;
+
+ spin_lock_nested(&child->d_lock, DENTRY_D_LOCK_NESTED);
+ child->d_flags |= DCACHE_FSNOTIFY_PARENT_WATCHED;
+ spin_unlock(&child->d_lock);
}
- spin_unlock(&inode->i_lock);
+ spin_unlock(&alias->d_lock);
+ dput(alias);
}

/*
--
2.50.0.107.gf914562f5916.dirty