diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2018-02-23 21:25:42 -0500 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2018-03-29 15:07:44 -0400 |
commit | f657a666fd1b1b9fe59963943c74c245ae66f4cc (patch) | |
tree | cf01b5b08611520699f8425b24029f7076a6357a /fs/dcache.c | |
parent | 62d9956cefe6ecc4b43a7fae37af78ba7adaceaa (diff) | |
download | lwn-f657a666fd1b1b9fe59963943c74c245ae66f4cc.tar.gz lwn-f657a666fd1b1b9fe59963943c74c245ae66f4cc.zip |
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 <viro@zeniv.linux.org.uk>
Diffstat (limited to 'fs/dcache.c')
-rw-r--r-- | fs/dcache.c | 34 |
1 files changed, 27 insertions, 7 deletions
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; } /* |