]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
primitives for maintaining persisitency
authorAl Viro <viro@zeniv.linux.org.uk>
Tue, 4 Mar 2025 00:38:04 +0000 (19:38 -0500)
committerAl Viro <viro@zeniv.linux.org.uk>
Sun, 16 Nov 2025 06:35:01 +0000 (01:35 -0500)
* d_make_persistent(dentry, inode) - bump refcount, mark persistent and
make hashed positive.  Return value is a borrowed reference to dentry;
it can be used until something removes persistency (at the very least,
until the parent gets unlocked, but some filesystems may have stronger
exclusion).

* d_make_discardable() - remove persistency mark and drop reference.

d_make_persistent() is similar to combination of d_instantiate(), dget()
and setting flag.  The only difference is that unlike d_instantiate()
it accepts hashed and unhashed negatives alike.  It is always called in
strong locking environment (parent held exclusive, or, in some cases,
dentry coming from d_alloc_name()); if we ever start using it with parent
held only shared and dentry coming from d_alloc_parallel(), we'll need
to copy the in-lookup logics from __d_add().

d_make_discardable() is eqiuvalent to combination of removing flag and
dput(); since flag removal requires ->d_lock, there's no point trying
to avoid taking that for refcount decrement as fast_dput() does.
The slow path of dput() has been taken into a helper and reused in
d_make_discardable() instead.

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
fs/dcache.c
include/linux/dcache.h

index f2c9f4fef2a2f0a09624341877eae9510044ef85..3cc6c3876177b25b6ecccd1ceddefc291fccdeec 100644 (file)
@@ -869,6 +869,24 @@ locked:
        return false;
 }
 
+static void finish_dput(struct dentry *dentry)
+       __releases(dentry->d_lock)
+       __releases(RCU)
+{
+       while (lock_for_kill(dentry)) {
+               rcu_read_unlock();
+               dentry = __dentry_kill(dentry);
+               if (!dentry)
+                       return;
+               if (retain_dentry(dentry, true)) {
+                       spin_unlock(&dentry->d_lock);
+                       return;
+               }
+               rcu_read_lock();
+       }
+       rcu_read_unlock();
+       spin_unlock(&dentry->d_lock);
+}
 
 /* 
  * This is dput
@@ -906,22 +924,28 @@ void dput(struct dentry *dentry)
                rcu_read_unlock();
                return;
        }
-       while (lock_for_kill(dentry)) {
-               rcu_read_unlock();
-               dentry = __dentry_kill(dentry);
-               if (!dentry)
-                       return;
-               if (retain_dentry(dentry, true)) {
-                       spin_unlock(&dentry->d_lock);
-                       return;
-               }
-               rcu_read_lock();
-       }
-       rcu_read_unlock();
-       spin_unlock(&dentry->d_lock);
+       finish_dput(dentry);
 }
 EXPORT_SYMBOL(dput);
 
+void d_make_discardable(struct dentry *dentry)
+{
+       spin_lock(&dentry->d_lock);
+       /*
+        * By the end of the series we'll add
+        * WARN_ON(!(dentry->d_flags & DCACHE_PERSISTENT);
+        * here, but while object removal is done by a few common helpers,
+        * object creation tends to be open-coded (if nothing else, new inode
+        * needs to be set up), so adding a warning from the very beginning
+        * would make for much messier patch series.
+        */
+       dentry->d_flags &= ~DCACHE_PERSISTENT;
+       dentry->d_lockref.count--;
+       rcu_read_lock();
+       finish_dput(dentry);
+}
+EXPORT_SYMBOL(d_make_discardable);
+
 static void to_shrink_list(struct dentry *dentry, struct list_head *list)
 __must_hold(&dentry->d_lock)
 {
@@ -1939,7 +1963,6 @@ static void __d_instantiate(struct dentry *dentry, struct inode *inode)
        unsigned add_flags = d_flags_for_inode(inode);
        WARN_ON(d_in_lookup(dentry));
 
-       spin_lock(&dentry->d_lock);
        /*
         * The negative counter only tracks dentries on the LRU. Don't dec if
         * d_lru is on another list.
@@ -1952,7 +1975,6 @@ static void __d_instantiate(struct dentry *dentry, struct inode *inode)
        __d_set_inode_and_type(dentry, inode, add_flags);
        raw_write_seqcount_end(&dentry->d_seq);
        fsnotify_update_flags(dentry);
-       spin_unlock(&dentry->d_lock);
 }
 
 /**
@@ -1976,7 +1998,9 @@ void d_instantiate(struct dentry *entry, struct inode * inode)
        if (inode) {
                security_d_instantiate(entry, inode);
                spin_lock(&inode->i_lock);
+               spin_lock(&entry->d_lock);
                __d_instantiate(entry, inode);
+               spin_unlock(&entry->d_lock);
                spin_unlock(&inode->i_lock);
        }
 }
@@ -1995,7 +2019,9 @@ void d_instantiate_new(struct dentry *entry, struct inode *inode)
        lockdep_annotate_inode_mutex_key(inode);
        security_d_instantiate(entry, inode);
        spin_lock(&inode->i_lock);
+       spin_lock(&entry->d_lock);
        __d_instantiate(entry, inode);
+       spin_unlock(&entry->d_lock);
        WARN_ON(!(inode->i_state & I_NEW));
        inode->i_state &= ~I_NEW & ~I_CREATING;
        /*
@@ -2754,6 +2780,24 @@ void d_add(struct dentry *entry, struct inode *inode)
 }
 EXPORT_SYMBOL(d_add);
 
+struct dentry *d_make_persistent(struct dentry *dentry, struct inode *inode)
+{
+       WARN_ON(!hlist_unhashed(&dentry->d_u.d_alias));
+       WARN_ON(!inode);
+       security_d_instantiate(dentry, inode);
+       spin_lock(&inode->i_lock);
+       spin_lock(&dentry->d_lock);
+       __d_instantiate(dentry, inode);
+       dentry->d_flags |= DCACHE_PERSISTENT;
+       dget_dlock(dentry);
+       if (d_unhashed(dentry))
+               __d_rehash(dentry);
+       spin_unlock(&dentry->d_lock);
+       spin_unlock(&inode->i_lock);
+       return dentry;
+}
+EXPORT_SYMBOL(d_make_persistent);
+
 static void swap_names(struct dentry *dentry, struct dentry *target)
 {
        if (unlikely(dname_external(target))) {
index 94b58655322a0a973f380f641b5d36e0b6f850da..6ec4066825e315d8a4c328503983ba03733299f1 100644 (file)
@@ -611,5 +611,7 @@ static inline struct dentry *d_next_sibling(const struct dentry *dentry)
 }
 
 void set_default_d_op(struct super_block *, const struct dentry_operations *);
+struct dentry *d_make_persistent(struct dentry *, struct inode *);
+void d_make_discardable(struct dentry *dentry);
 
 #endif /* __LINUX_DCACHE_H */