From mboxrd@z Thu Jan 1 00:00:00 1970 From: Cong Wang Subject: [Patch 3.14 stable 13/16] dealing with the rest of shrink_dentry_list() livelock Date: Thu, 6 Nov 2014 11:37:17 -0800 Message-ID: <1415302640-5876-14-git-send-email-xiyou.wangcong@gmail.com> References: <1415302640-5876-1-git-send-email-xiyou.wangcong@gmail.com> Cc: viro@zeniv.linux.org.uk, gregkh@linuxfoundation.org, linux-fsdevel@vger.kernel.org To: stable@vger.kernel.org Return-path: Received: from mail-pd0-f174.google.com ([209.85.192.174]:39722 "EHLO mail-pd0-f174.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751694AbaKFTh5 (ORCPT ); Thu, 6 Nov 2014 14:37:57 -0500 In-Reply-To: <1415302640-5876-1-git-send-email-xiyou.wangcong@gmail.com> Sender: linux-fsdevel-owner@vger.kernel.org List-ID: From: Al Viro We have the same problem with ->d_lock order in the inner loop, where we are dropping references to ancestors. Same solution, basically - instead of using dentry_kill() we use lock_parent() (introduced in the previous commit) to get that lock in a safe way, recheck ->d_count (in case if lock_parent() has ended up dropping and retaking ->d_lock and somebody managed to grab a reference during that window), trylock the inode->i_lock and use __dentry_kill() to do the rest. Signed-off-by: Al Viro (cherry picked from commit b2b80195d8829921506880f6dccd21cabd163d0d) --- fs/dcache.c | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/fs/dcache.c b/fs/dcache.c index 1c1b14c..1b9f89f 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -884,8 +884,26 @@ static void shrink_dentry_list(struct list_head *list) * fragmentation. */ dentry = parent; - while (dentry && !lockref_put_or_lock(&dentry->d_lockref)) - dentry = dentry_kill(dentry, 1); + while (dentry && !lockref_put_or_lock(&dentry->d_lockref)) { + parent = lock_parent(dentry); + if (dentry->d_lockref.count != 1) { + dentry->d_lockref.count--; + spin_unlock(&dentry->d_lock); + if (parent) + spin_unlock(&parent->d_lock); + break; + } + inode = dentry->d_inode; /* can't be NULL */ + if (unlikely(!spin_trylock(&inode->i_lock))) { + spin_unlock(&dentry->d_lock); + if (parent) + spin_unlock(&parent->d_lock); + cpu_relax(); + continue; + } + __dentry_kill(dentry); + dentry = parent; + } } } -- 1.8.3.1