It is possible to run dput without taking locks up-front. In many cases where we don't kill the dentry anyway, these locks are not required. (I think... need to think about it more). Further changes ->d_delete locking which is not all audited. d_delete must be idempotent, for one. Signed-off-by: Nick Piggin --- fs/dcache.c | 60 ++++++++++++++++++++++++++++++++---------------------------- 1 file changed, 32 insertions(+), 28 deletions(-) Index: linux-2.6/fs/dcache.c =================================================================== --- linux-2.6.orig/fs/dcache.c +++ linux-2.6/fs/dcache.c @@ -278,7 +278,8 @@ static struct dentry *d_kill(struct dent void dput(struct dentry *dentry) { - struct dentry *parent = NULL; + struct dentry *parent; + if (!dentry) return; @@ -286,25 +287,10 @@ repeat: if (dentry->d_count == 1) might_sleep(); spin_lock(&dentry->d_lock); - if (dentry->d_count == 1) { - if (!spin_trylock(&dcache_inode_lock)) { -drop2: - spin_unlock(&dentry->d_lock); - goto repeat; - } - parent = dentry->d_parent; - if (parent && parent != dentry) { - if (!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 && parent != dentry) - spin_unlock(&parent->d_lock); return; } @@ -312,8 +298,10 @@ drop2: * 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; + if (dentry->d_op->d_delete(dentry)) { + __d_drop(dentry); + goto kill_it; + } } /* Unreachable? Get rid of it */ if (d_unhashed(dentry)) @@ -322,15 +310,31 @@ drop2: dentry->d_flags |= DCACHE_REFERENCED; dentry_lru_add(dentry); } - spin_unlock(&dentry->d_lock); - if (parent && parent != dentry) - spin_unlock(&parent->d_lock); - spin_unlock(&dcache_inode_lock); - return; + dentry->d_count--; + spin_unlock(&dentry->d_lock); + return; -unhash_it: - __d_drop(dentry); kill_it: + spin_unlock(&dentry->d_lock); + spin_lock(&dcache_inode_lock); +relock: + spin_lock(&dentry->d_lock); + parent = dentry->d_parent; + if (parent && parent != dentry) { + if (!spin_trylock(&parent->d_lock)) { + spin_unlock(&dentry->d_lock); + goto relock; + } + } + dentry->d_count--; + if (dentry->d_count) { + /* This case should be fine */ + spin_unlock(&dentry->d_lock); + if (parent && parent != dentry) + spin_unlock(&parent->d_lock); + spin_unlock(&dcache_inode_lock); + return; + } /* if dentry was on the d_lru list delete it from there */ dentry_lru_del(dentry); dentry = d_kill(dentry); -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/