* 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 <[email protected]>
---
 fs/dcache.c            | 66 ++++++++++++++++++++++++++++++++----------
 include/linux/dcache.h |  2 ++
 2 files changed, 53 insertions(+), 15 deletions(-)

diff --git a/fs/dcache.c b/fs/dcache.c
index f2c9f4fef2a2..3e26039ceca1 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -869,6 +869,24 @@ static inline bool fast_dput(struct dentry *dentry)
        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,20 @@ 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);
+       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 +1955,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 +1967,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 +1990,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 +2011,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 +2772,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))) {
diff --git a/include/linux/dcache.h b/include/linux/dcache.h
index 94b58655322a..6ec4066825e3 100644
--- a/include/linux/dcache.h
+++ b/include/linux/dcache.h
@@ -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 */
-- 
2.47.3


Reply via email to