[Devel] [PATCH rh7 2/3] dcache: don't remove from shrink list in select_collect()

Vladimir Davydov vdavydov at virtuozzo.com
Mon Feb 15 08:14:32 PST 2016


From: Al Viro <viro at zeniv.linux.org.uk>

Backport mainstream commit:

  commit fe91522a7ba82ca1a51b07e19954b3825e4aaa22
  Author: Al Viro <viro at zeniv.linux.org.uk>
  Date:   Sat May 3 00:02:25 2014 -0400

      don't remove from shrink list in select_collect()

          If we find something already on a shrink list, just increment
      data->found and do nothing else.  Loops in shrink_dcache_parent() and
      check_submounts_and_drop() will do the right thing - everything we
      did put into our list will be evicted and if there had been nothing,
      but data->found got non-zero, well, we have somebody else shrinking
      those guys; just try again.

      Signed-off-by: Al Viro <viro at zeniv.linux.org.uk>

Dcache shrinker temporarily adds dentries to a shrink list before
dropping them. This list is allocated on stack and not protected by any
locks, therefore only the caller (i.e. the process scanning dcache) can
safely remove entries from it. Currently, however, dentries can be
removed from a shrink list by dentry_kill() and select_collect(). If
this races with a shrinker invocation, we'll get list corruption:

  WARNING: at lib/list_debug.c:59 __list_del_entry+0xa1/0xd0()
  list_del corruption. prev->next should be ffff8801e1621980, but was ffff8801de3155c0
  CPU: 5 PID: 58031 Comm: trinity-main ve: 101 Not tainted 3.10.0-327.3.1.vz7.10.11 #1 10.11
   000000000000003b 00000000d7f09cf1 ffff8800a3ca3b80 ffffffff816306f3
   ffff8800a3ca3bb8 ffffffff8107b4c0 ffff8801e1621900 ffff8801e1621980
   ffff880207c172b8 ffff8801e27069c0 0000000000000000 ffff8800a3ca3c20
  Call Trace:
   [<ffffffff816306f3>] dump_stack+0x19/0x1b
   [<ffffffff8107b4c0>] warn_slowpath_common+0x70/0xb0
   [<ffffffff8107b55c>] warn_slowpath_fmt+0x5c/0x80
   [<ffffffff8130a0f1>] __list_del_entry+0xa1/0xd0
   [<ffffffff8121083c>] d_shrink_del+0x2c/0x80
   [<ffffffff81210fe5>] dentry_lru_del+0x25/0x30
   [<ffffffff812116f1>] dput+0x161/0x280
   [<ffffffff812057f5>] lookup_fast+0x275/0x2e0
   [<ffffffff8120833c>] path_lookupat+0x16c/0x7a0
   [<ffffffff81214dc7>] ? inode_init_always+0x107/0x1e0
   [<ffffffff811db380>] ? kmem_cache_alloc+0xf0/0x220
   [<ffffffff8120ab5f>] ? getname_flags+0x4f/0x1a0
   [<ffffffff8120899b>] filename_lookup+0x2b/0xc0
   [<ffffffff8120bc87>] user_path_at_empty+0x67/0xc0
   [<ffffffff81112962>] ? from_kgid_munged+0x12/0x20
   [<ffffffff811ff9e9>] ? cp_new_stat+0x149/0x180
   [<ffffffff8120bcf1>] user_path_at+0x11/0x20
   [<ffffffff811ff4f3>] vfs_fstatat+0x63/0xc0
   [<ffffffff811ffb04>] SYSC_newfstatat+0x24/0x60
   [<ffffffff8111c724>] ? __audit_syscall_entry+0xb4/0x110
   [<ffffffff81022923>] ? syscall_trace_enter+0x173/0x220
   [<ffffffff81640ff3>] ? tracesys+0x7e/0xe2
   [<ffffffff811ffd4e>] SyS_newfstatat+0xe/0x10
   [<ffffffff81641052>] tracesys+0xdd/0xe2

https://jira.sw.ru/browse/PSBM-44210

Signed-off-by: Vladimir Davydov <vdavydov at virtuozzo.com>
---
 fs/dcache.c | 31 ++++++++++---------------------
 1 file changed, 10 insertions(+), 21 deletions(-)

diff --git a/fs/dcache.c b/fs/dcache.c
index 30d3d706f32e..cba54cf702f5 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -1414,34 +1414,23 @@ static enum d_walk_ret select_collect(void *_data, struct dentry *dentry)
 	if (data->start == dentry)
 		goto out;
 
-	/*
-	 * move only zero ref count dentries to the dispose list.
-	 *
-	 * Those which are presently on the shrink list, being processed
-	 * by shrink_dentry_list(), shouldn't be moved.  Otherwise the
-	 * loop in shrink_dcache_parent() might not make any progress
-	 * and loop forever.
-	 */
-	if (dentry->d_lockref.count) {
-		dentry_lru_del(dentry);
-	} else if (!(dentry->d_flags & DCACHE_SHRINK_LIST)) {
-		/*
-		 * We can't use d_lru_shrink_move() because we
-		 * need to get the global LRU lock and do the
-		 * RLU accounting.
-		 */
-		d_lru_del(dentry);
-		d_shrink_add(dentry, &data->dispose);
+	if (dentry->d_flags & DCACHE_SHRINK_LIST) {
 		data->found++;
-		ret = D_WALK_NORETRY;
+	} else {
+		if (dentry->d_flags & DCACHE_LRU_LIST)
+			d_lru_del(dentry);
+		if (!dentry->d_lockref.count) {
+			d_shrink_add(dentry, &data->dispose);
+			data->found++;
+		}
 	}
 	/*
 	 * We can return to the caller if we have found some (this
 	 * ensures forward progress). We'll be coming back to find
 	 * the rest.
 	 */
-	if (data->found && need_resched())
-		ret = D_WALK_QUIT;
+	if (!list_empty(&data->dispose))
+		ret = need_resched() ? D_WALK_QUIT : D_WALK_NORETRY;
 out:
 	return ret;
 }
-- 
2.1.4



More information about the Devel mailing list