[Devel] [PATCH rh7 1/3] dcache: dentry_kill(): don't try to remove from shrink list

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


Backport mainstream commit:

  commit 41edf278fc2f042f4e22a12ed87d19c5201210e1
  Author: Al Viro <viro at zeniv.linux.org.uk>
  Date:   Thu May 1 10:30:00 2014 -0400

      dentry_kill(): don't try to remove from shrink list

      If the victim in on the shrink list, don't remove it from there.
      If shrink_dentry_list() manages to remove it from the list before
      we are done - fine, we'll just free it as usual.  If not - mark
      it with new flag (DCACHE_MAY_FREE) and leave it there.

      Eventually, shrink_dentry_list() will get to it, remove the sucker
      from shrink list and call dentry_kill(dentry, 0).  Which is where
      we'll deal with freeing.

      Since now dentry_kill(dentry, 0) may happen after or during
      dentry_kill(dentry, 1), we need to recognize that (by seeing
      DCACHE_DENTRY_KILLED already set), unlock everything
      and either free the sucker (in case DCACHE_MAY_FREE has been
      set) or leave it for ongoing dentry_kill(dentry, 1) to deal with.

      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            | 41 ++++++++++++++++++++++++++++++++++-------
 include/linux/dcache.h |  2 ++
 2 files changed, 36 insertions(+), 7 deletions(-)

diff --git a/fs/dcache.c b/fs/dcache.c
index d026a22e8050..30d3d706f32e 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -252,22 +252,38 @@ static void __d_free(struct rcu_head *head)
 	kmem_cache_free(dentry_cache, dentry); 
 }
 
+static void dentry_free(struct dentry *dentry)
+{
+	struct rcu_head *p = (struct rcu_head *)&dentry->d_alias;
+
+	/* if dentry was never visible to RCU, immediate free is OK */
+	if (!(dentry->d_flags & DCACHE_RCUACCESS))
+		__d_free(p);
+	else
+		call_rcu(p, __d_free);
+}
+
 /*
  * no locks, please.
  */
 static void d_free(struct dentry *dentry)
 {
-	struct rcu_head *p = (struct rcu_head *)&dentry->d_alias;
+	bool can_free = true;
+
 	BUG_ON((int)dentry->d_lockref.count > 0);
 	this_cpu_dec(nr_dentry);
 	if (dentry->d_op && dentry->d_op->d_release)
 		dentry->d_op->d_release(dentry);
 
-	/* if dentry was never visible to RCU, immediate free is OK */
-	if (!(dentry->d_flags & DCACHE_RCUACCESS))
-		__d_free(p);
-	else
-		call_rcu(p, __d_free);
+	spin_lock(&dentry->d_lock);
+	if (dentry->d_flags & DCACHE_SHRINK_LIST) {
+		dentry->d_flags |= DCACHE_MAY_FREE;
+		can_free = false;
+	}
+	spin_unlock(&dentry->d_lock);
+
+	if (likely(can_free))
+		dentry_free(dentry);
 }
 
 /**
@@ -527,6 +543,14 @@ dentry_kill(struct dentry *dentry, int unlock_on_failure)
 	struct inode *inode;
 	struct dentry *parent;
 
+	if (unlikely(dentry->d_flags & DCACHE_DENTRY_KILLED)) {
+		bool can_free = dentry->d_flags & DCACHE_MAY_FREE;
+		spin_unlock(&dentry->d_lock);
+		if (likely(can_free))
+			dentry_free(dentry);
+		return NULL;
+	}
+
 	inode = dentry->d_inode;
 	if (inode && !spin_trylock(&inode->i_lock)) {
 relock:
@@ -558,7 +582,10 @@ dentry_kill(struct dentry *dentry, int unlock_on_failure)
 	if ((dentry->d_flags & DCACHE_OP_PRUNE) && !d_unhashed(dentry))
 		dentry->d_op->d_prune(dentry);
 
-	dentry_lru_del(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);
 	return d_kill(dentry, parent);
diff --git a/include/linux/dcache.h b/include/linux/dcache.h
index 4a44f379d3f4..51dffd24037b 100644
--- a/include/linux/dcache.h
+++ b/include/linux/dcache.h
@@ -220,6 +220,8 @@ struct dentry_operations {
 #define DCACHE_SYMLINK_TYPE		0x03000000 /* Symlink */
 #define DCACHE_FILE_TYPE		0x04000000 /* Other file type */
 
+#define DCACHE_MAY_FREE			0x00800000
+
 extern seqlock_t rename_lock;
 
 static inline int dname_external(struct dentry *dentry)
-- 
2.1.4



More information about the Devel mailing list