diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2021-01-05 14:13:52 -0500 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2021-01-16 15:12:06 -0500 |
commit | bca585d24a1719d9314d5438b0d2804a33d9bbb6 (patch) | |
tree | aa7d4a45ea39b63deb8e0ebbb82f1c9d2aa0ee0e /fs/dcache.c | |
parent | d36a1dd9f77ae1e72da48f4123ed35627848507d (diff) |
new helper: d_find_alias_rcu()
similar to d_find_alias(inode), except that
* the caller must be holding rcu_read_lock()
* inode must not be freed until matching rcu_read_unlock()
* result is *NOT* pinned and can only be dereferenced until
the matching rcu_read_unlock().
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'fs/dcache.c')
-rw-r--r-- | fs/dcache.c | 25 |
1 files changed, 25 insertions, 0 deletions
diff --git a/fs/dcache.c b/fs/dcache.c index 97e81a844a96..843546633457 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -1043,6 +1043,31 @@ struct dentry *d_find_alias(struct inode *inode) EXPORT_SYMBOL(d_find_alias); /* + * Caller MUST be holding rcu_read_lock() and be guaranteed + * that inode won't get freed until rcu_read_unlock(). + */ +struct dentry *d_find_alias_rcu(struct inode *inode) +{ + struct hlist_head *l = &inode->i_dentry; + struct dentry *de = NULL; + + spin_lock(&inode->i_lock); + // ->i_dentry and ->i_rcu are colocated, but the latter won't be + // used without having I_FREEING set, which means no aliases left + if (likely(!(inode->i_state & I_FREEING) && !hlist_empty(l))) { + if (S_ISDIR(inode->i_mode)) { + de = hlist_entry(l->first, struct dentry, d_u.d_alias); + } else { + hlist_for_each_entry(de, l, d_u.d_alias) + if (!d_unhashed(de)) + break; + } + } + spin_unlock(&inode->i_lock); + return de; +} + +/* * Try to kill dentries associated with this inode. * WARNING: you must own a reference to inode. */ |