get rid of trylock loop around dentry_kill()
authorAl Viro <viro@zeniv.linux.org.uk>
Sat, 24 Feb 2018 02:25:42 +0000 (21:25 -0500)
committerAl Viro <viro@zeniv.linux.org.uk>
Thu, 29 Mar 2018 19:07:44 +0000 (15:07 -0400)
In case when trylock in there fails, deal with it directly in
dentry_kill().  Note that in cases when we drop and retake
->d_lock, we need to recheck whether to retain the dentry.
Another thing is that dropping/retaking ->d_lock might have
ended up with negative dentry turning into positive; that,
of course, can happen only once...

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

index f5609902c6dd865ee19af484bba27440d3af9fad..f2d94568802513be38290abc52c5aa29f604a9af 100644 (file)
@@ -651,23 +651,43 @@ static struct dentry *dentry_kill(struct dentry *dentry)
        struct dentry *parent = NULL;
 
        if (inode && unlikely(!spin_trylock(&inode->i_lock)))
-               goto failed;
+               goto slow_positive;
 
        if (!IS_ROOT(dentry)) {
                parent = dentry->d_parent;
                if (unlikely(!spin_trylock(&parent->d_lock))) {
-                       if (inode)
-                               spin_unlock(&inode->i_lock);
-                       goto failed;
+                       parent = __lock_parent(dentry);
+                       if (likely(inode || !dentry->d_inode))
+                               goto got_locks;
+                       /* negative that became positive */
+                       if (parent)
+                               spin_unlock(&parent->d_lock);
+                       inode = dentry->d_inode;
+                       goto slow_positive;
                }
        }
-
        __dentry_kill(dentry);
        return parent;
 
-failed:
+slow_positive:
+       spin_unlock(&dentry->d_lock);
+       spin_lock(&inode->i_lock);
+       spin_lock(&dentry->d_lock);
+       parent = lock_parent(dentry);
+got_locks:
+       if (unlikely(dentry->d_lockref.count != 1)) {
+               dentry->d_lockref.count--;
+       } else if (likely(!retain_dentry(dentry))) {
+               __dentry_kill(dentry);
+               return parent;
+       }
+       /* we are keeping it, after all */
+       if (inode)
+               spin_unlock(&inode->i_lock);
+       if (parent)
+               spin_unlock(&parent->d_lock);
        spin_unlock(&dentry->d_lock);
-       return dentry; /* try again with same dentry */
+       return NULL;
 }
 
 /*