Pull dropping ->d_lock on lock_for_kill() failure into lock_for_kill() itself. That reduces dentry_kill() to if (!lock_for_kill(dentry)) return NULL; return __dentry_kill(dentry); at which point it's easier to move that if (...) into the beginning of __dentry_kill() itself and rename it into dentry_kill(). Document the new calling conventions of lock_for_kill(). Signed-off-by: Al Viro --- fs/dcache.c | 112 +++++++++++++++++++++++++--------------------------- 1 file changed, 54 insertions(+), 58 deletions(-) diff --git a/fs/dcache.c b/fs/dcache.c index a65cb6451e63..3887d8b5f81a 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -691,57 +691,6 @@ static inline void dentry_unlist(struct dentry *dentry) } } -static struct dentry *__dentry_kill(struct dentry *dentry) -{ - struct dentry *parent = NULL; - bool can_free = true; - - /* - * The dentry is now unrecoverably dead to the world. - */ - lockref_mark_dead(&dentry->d_lockref); - - /* - * inform the fs via d_prune that this dentry is about to be - * unhashed and destroyed. - */ - if (dentry->d_flags & DCACHE_OP_PRUNE) - dentry->d_op->d_prune(dentry); - - if (dentry->d_flags & DCACHE_LRU_LIST) { - if (!(dentry->d_flags & DCACHE_SHRINK_LIST)) - d_lru_del(dentry); - } - /* if it was on the hash then remove it */ - __d_drop(dentry); - if (dentry->d_inode) - dentry_unlink_inode(dentry); - else - spin_unlock(&dentry->d_lock); - this_cpu_dec(nr_dentry); - if (dentry->d_op && dentry->d_op->d_release) - dentry->d_op->d_release(dentry); - - cond_resched(); - /* now that it's negative, ->d_parent is stable */ - if (!IS_ROOT(dentry)) { - parent = dentry->d_parent; - spin_lock(&parent->d_lock); - } - spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED); - dentry_unlist(dentry); - if (dentry->d_flags & DCACHE_SHRINK_LIST) - can_free = false; - spin_unlock(&dentry->d_lock); - if (likely(can_free)) - dentry_free(dentry); - if (parent && --parent->d_lockref.count) { - spin_unlock(&parent->d_lock); - return NULL; - } - return parent; -} - /* * Prepare locking environment for killing a dentry. * Called under dentry->d_lock. To proceed with eviction of a positive dentry @@ -763,17 +712,18 @@ static struct dentry *__dentry_kill(struct dentry *dentry) * provided by keeping the RCU read-side critical area contiguous with * an explicit rcu_read_lock() scope bridging over the break in spinlock scopes. * - * Return false if dentry is busy (or busy dying, or already dead). Otherwise, - * return true and have that dentry's inode (if any) locked in addition to - * dentry itself. + * If dentry is busy (or busy dying, or already dead), unlock dentry + * and return false. Otherwise, return true and have that dentry's + * inode (if any) locked in addition to dentry itself. */ - static bool lock_for_kill(struct dentry *dentry) { struct inode *inode = dentry->d_inode; - if (unlikely(dentry->d_lockref.count)) + if (unlikely(dentry->d_lockref.count)) { + spin_unlock(&dentry->d_lock); return false; + } if (!inode || likely(spin_trylock(&inode->i_lock))) return true; @@ -798,16 +748,62 @@ static bool lock_for_kill(struct dentry *dentry) return true; if (inode) spin_unlock(&inode->i_lock); + spin_unlock(&dentry->d_lock); return false; } static struct dentry *dentry_kill(struct dentry *dentry) { - if (unlikely(!lock_for_kill(dentry))) { + struct dentry *parent = NULL; + bool can_free = true; + + if (unlikely(!lock_for_kill(dentry))) + return NULL; + + /* + * The dentry is now unrecoverably dead to the world. + */ + lockref_mark_dead(&dentry->d_lockref); + + /* + * inform the fs via d_prune that this dentry is about to be + * unhashed and destroyed. + */ + if (dentry->d_flags & DCACHE_OP_PRUNE) + dentry->d_op->d_prune(dentry); + + if (dentry->d_flags & DCACHE_LRU_LIST) { + if (!(dentry->d_flags & DCACHE_SHRINK_LIST)) + d_lru_del(dentry); + } + /* if it was on the hash then remove it */ + __d_drop(dentry); + if (dentry->d_inode) + dentry_unlink_inode(dentry); + else spin_unlock(&dentry->d_lock); + this_cpu_dec(nr_dentry); + if (dentry->d_op && dentry->d_op->d_release) + dentry->d_op->d_release(dentry); + + cond_resched(); + /* now that it's negative, ->d_parent is stable */ + if (!IS_ROOT(dentry)) { + parent = dentry->d_parent; + spin_lock(&parent->d_lock); + } + spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED); + dentry_unlist(dentry); + if (dentry->d_flags & DCACHE_SHRINK_LIST) + can_free = false; + spin_unlock(&dentry->d_lock); + if (likely(can_free)) + dentry_free(dentry); + if (parent && --parent->d_lockref.count) { + spin_unlock(&parent->d_lock); return NULL; } - return __dentry_kill(dentry); + return parent; } /* -- 2.47.3