From: Al Viro Date: Sat, 24 Feb 2018 02:25:42 +0000 (-0500) Subject: get rid of trylock loop around dentry_kill() X-Git-Url: http://git.cdn.openwrt.org/?a=commitdiff_plain;h=f657a666fd1b1b9fe59963943c74c245ae66f4cc;p=openwrt%2Fstaging%2Fblogic.git get rid of trylock loop around dentry_kill() 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 --- diff --git a/fs/dcache.c b/fs/dcache.c index f5609902c6dd..f2d945688025 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -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; } /*