summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAl Viro <viro@zeniv.linux.org.uk>2021-01-05 14:13:52 -0500
committerAl Viro <viro@zeniv.linux.org.uk>2021-01-16 15:12:06 -0500
commitbca585d24a1719d9314d5438b0d2804a33d9bbb6 (patch)
treeaa7d4a45ea39b63deb8e0ebbb82f1c9d2aa0ee0e
parentd36a1dd9f77ae1e72da48f4123ed35627848507d (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>
-rw-r--r--fs/dcache.c25
-rw-r--r--include/linux/dcache.h2
2 files changed, 27 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.
*/
diff --git a/include/linux/dcache.h b/include/linux/dcache.h
index d7b369fc15d3..c1e48014106f 100644
--- a/include/linux/dcache.h
+++ b/include/linux/dcache.h
@@ -262,6 +262,8 @@ extern void d_tmpfile(struct dentry *, struct inode *);
extern struct dentry *d_find_alias(struct inode *);
extern void d_prune_aliases(struct inode *);
+extern struct dentry *d_find_alias_rcu(struct inode *);
+
/* test whether we have any submounts in a subdir tree */
extern int path_has_submounts(const struct path *);