[PATCH v1 04/12] fsnotify: reduce i_lock hold time in fsnotify_set_children_dentry_flags()
NeilBrown <[email protected]> Mon, 3 Aug 2026 11:21:13 +1000
| Newsgroups | org.kernel.vger.linux-nfs,org.kernel.vger.autofs,org.kernel.vger.ceph-devel,org.kernel.vger.linux-fsdevel,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <[email protected]> |
From: NeilBrown <[email protected]> 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 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 <[email protected]> --- fs/notify/fsnotify.c | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c index 90a2121fc54a..aa94c0fa7686 100644 --- a/fs/notify/fsnotify.c +++ b/fs/notify/fsnotify.c @@ -71,27 +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) */ - d_for_each_positive_child(child, alias) { - spin_lock_nested(&child->d_lock, DENTRY_D_LOCK_NESTED); - child->d_flags |= DCACHE_FSNOTIFY_PARENT_WATCHED; - spin_unlock(&child->d_lock); - } - } + /* Find the dentry for inode - there can only be one */ + for_each_alias(alias, inode) + break; + dget(alias); spin_unlock(&inode->i_lock); + + if (!alias) + return; + + /* + * run all of the children of the original inode and fix their + * d_flags to indicate parental interest (their parent is the + * original inode) + */ + d_for_each_positive_child(child, alias) { + spin_lock_nested(&child->d_lock, DENTRY_D_LOCK_NESTED); + child->d_flags |= DCACHE_FSNOTIFY_PARENT_WATCHED; + spin_unlock(&child->d_lock); + } + dput(alias); } /* -- 2.50.0.107.gf914562f5916.dirty