aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorAl Viro <viro@zeniv.linux.org.uk>2014-05-29 09:11:45 -0400
committerAl Viro <viro@zeniv.linux.org.uk>2014-05-30 11:10:33 -0400
commitb2b80195d8829921506880f6dccd21cabd163d0d (patch)
tree4c0845c5bdbcfba145459fb1a5f9853f762fb64c /fs
parent046b961b45f93a92e4c70525a12f3d378bced130 (diff)
dealing with the rest of shrink_dentry_list() livelock
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 <viro@zeniv.linux.org.uk>
Diffstat (limited to 'fs')
-rw-r--r--fs/dcache.c22
1 files changed, 20 insertions, 2 deletions
diff --git a/fs/dcache.c b/fs/dcache.c
index d54a99baf4f3..eb7c7255470c 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -885,8 +885,26 @@ static void shrink_dentry_list(struct list_head *list)
885 * fragmentation. 885 * fragmentation.
886 */ 886 */
887 dentry = parent; 887 dentry = parent;
888 while (dentry && !lockref_put_or_lock(&dentry->d_lockref)) 888 while (dentry && !lockref_put_or_lock(&dentry->d_lockref)) {
889 dentry = dentry_kill(dentry, 1); 889 parent = lock_parent(dentry);
890 if (dentry->d_lockref.count != 1) {
891 dentry->d_lockref.count--;
892 spin_unlock(&dentry->d_lock);
893 if (parent)
894 spin_unlock(&parent->d_lock);
895 break;
896 }
897 inode = dentry->d_inode; /* can't be NULL */
898 if (unlikely(!spin_trylock(&inode->i_lock))) {
899 spin_unlock(&dentry->d_lock);
900 if (parent)
901 spin_unlock(&parent->d_lock);
902 cpu_relax();
903 continue;
904 }
905 __dentry_kill(dentry);
906 dentry = parent;
907 }
890 } 908 }
891} 909}
892 910