fs: dcache reduce dput locking
authorNick Piggin <npiggin@kernel.dk>
Fri, 7 Jan 2011 06:49:40 +0000 (17:49 +1100)
committerNick Piggin <npiggin@kernel.dk>
Fri, 7 Jan 2011 06:50:23 +0000 (17:50 +1100)
It is possible to run dput without taking data structure locks up-front. In
many cases where we don't kill the dentry anyway, these locks are not required.

Signed-off-by: Nick Piggin <npiggin@kernel.dk>
fs/dcache.c

index bf6294a20f0ebff37dfc0308e221c1df14029420..98a05696593e89c23231476c4cf26e3bf006df2d 100644 (file)
@@ -323,35 +323,16 @@ repeat:
        if (dentry->d_count == 1)
                might_sleep();
        spin_lock(&dentry->d_lock);
-       if (IS_ROOT(dentry))
-               parent = NULL;
-       else
-               parent = dentry->d_parent;
-       if (dentry->d_count == 1) {
-               if (!spin_trylock(&dcache_inode_lock)) {
-drop2:
-                       spin_unlock(&dentry->d_lock);
-                       goto repeat;
-               }
-               if (parent && !spin_trylock(&parent->d_lock)) {
-                       spin_unlock(&dcache_inode_lock);
-                       goto drop2;
-               }
-       }
-       dentry->d_count--;
-       if (dentry->d_count) {
+       BUG_ON(!dentry->d_count);
+       if (dentry->d_count > 1) {
+               dentry->d_count--;
                spin_unlock(&dentry->d_lock);
-               if (parent)
-                       spin_unlock(&parent->d_lock);
                return;
        }
 
-       /*
-        * AV: ->d_delete() is _NOT_ allowed to block now.
-        */
        if (dentry->d_op && dentry->d_op->d_delete) {
                if (dentry->d_op->d_delete(dentry))
-                       goto unhash_it;
+                       goto kill_it;
        }
 
        /* Unreachable? Get rid of it */
@@ -362,17 +343,30 @@ drop2:
        dentry->d_flags |= DCACHE_REFERENCED;
        dentry_lru_add(dentry);
 
-       spin_unlock(&dentry->d_lock);
-       if (parent)
-               spin_unlock(&parent->d_lock);
-       spin_unlock(&dcache_inode_lock);
+       dentry->d_count--;
+       spin_unlock(&dentry->d_lock);
        return;
 
-unhash_it:
-       __d_drop(dentry);
 kill_it:
+       if (!spin_trylock(&dcache_inode_lock)) {
+relock:
+               spin_unlock(&dentry->d_lock);
+               cpu_relax();
+               goto repeat;
+       }
+       if (IS_ROOT(dentry))
+               parent = NULL;
+       else
+               parent = dentry->d_parent;
+       if (parent && !spin_trylock(&parent->d_lock)) {
+               spin_unlock(&dcache_inode_lock);
+               goto relock;
+       }
+       dentry->d_count--;
        /* if dentry was on the d_lru list delete it from there */
        dentry_lru_del(dentry);
+       /* if it was on the hash (d_delete case), then remove it */
+       __d_drop(dentry);
        dentry = d_kill(dentry, parent);
        if (dentry)
                goto repeat;