]> git.baikalelectronics.ru Git - kernel.git/commitdiff
new helper: d_find_alias_rcu()
authorAl Viro <viro@zeniv.linux.org.uk>
Tue, 5 Jan 2021 19:13:52 +0000 (14:13 -0500)
committerAl Viro <viro@zeniv.linux.org.uk>
Sat, 16 Jan 2021 20:12:06 +0000 (15:12 -0500)
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>
fs/dcache.c
include/linux/dcache.h

index 97e81a844a966c6683e4688347f78149f886e16f..843546633457036419349a32cc1cce4171fd0923 100644 (file)
@@ -1042,6 +1042,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.
index d7b369fc15d36bb34d58f213765ccb4228d819db..c1e48014106fa3aad2d6f8d5b4e0cf2f54ce9bc2 100644 (file)
@@ -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 *);