Home Home > GIT Browse
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNeilBrown <neilb@suse.com>2018-11-01 14:04:41 +1100
committerNeilBrown <neilb@suse.com>2018-11-01 14:05:08 +1100
commit3252d2eeebb8b92987e95a3162e35dfa0c0d8034 (patch)
tree80982e75e8c495cb9aadd02bdb15cdc79d87d91b
parent89f81b69fd1635e2e3902196087d722550a8400f (diff)
fs: dcache: Avoid livelock between d_alloc_parallel and __d_add
(git-fixes).
-rw-r--r--patches.fixes/fs-dcache-Avoid-livelock-between-d_alloc_parallel-an.patch79
-rw-r--r--series.conf1
2 files changed, 80 insertions, 0 deletions
diff --git a/patches.fixes/fs-dcache-Avoid-livelock-between-d_alloc_parallel-an.patch b/patches.fixes/fs-dcache-Avoid-livelock-between-d_alloc_parallel-an.patch
new file mode 100644
index 0000000000..300f0ed811
--- /dev/null
+++ b/patches.fixes/fs-dcache-Avoid-livelock-between-d_alloc_parallel-an.patch
@@ -0,0 +1,79 @@
+From: Will Deacon <will.deacon@arm.com>
+Date: Mon, 19 Feb 2018 14:55:54 +0000
+Subject: [PATCH] fs: dcache: Avoid livelock between d_alloc_parallel and
+ __d_add
+Git-commit: 015555fd4d2930bc0c86952c46ad88b3392f66e4
+Patch-mainline: v4.16
+References: git-fixes
+
+If d_alloc_parallel runs concurrently with __d_add, it is possible for
+d_alloc_parallel to continuously retry whilst i_dir_seq has been
+incremented to an odd value by __d_add:
+
+Cpu0:
+__d_add
+ n = start_dir_add(dir);
+ cmpxchg(&dir->i_dir_seq, n, n + 1) == n
+
+Cpu1:
+d_alloc_parallel
+Retry: seq = smp_load_acquire(&parent->d_inode->i_dir_seq) & ~1;
+ hlist_bl_lock(b);
+ bit_spin_lock(0, (unsigned long *)b); // Always succeeds
+
+Cpu0: __d_lookup_done(dentry)
+ hlist_bl_lock
+ bit_spin_lock(0, (unsigned long *)b); // Never succeeds
+
+Cpu1: if (unlikely(parent->d_inode->i_dir_seq != seq)) {
+ hlist_bl_unlock(b);
+ goto retry;
+ }
+
+Since the simple bit_spin_lock used to implement hlist_bl_lock does not
+provide any fairness guarantees, then CPU1 can starve CPU0 of the lock
+and prevent it from reaching end_dir_add(dir), therefore CPU1 cannot
+exit its retry loop because the sequence number always has the bottom
+bit set.
+
+This patch resolves the livelock by not taking hlist_bl_lock in
+d_alloc_parallel if the sequence counter is odd, since any subsequent
+masked comparison with i_dir_seq will fail anyway.
+
+Cc: Peter Zijlstra <peterz@infradead.org>
+Cc: Al Viro <viro@zeniv.linux.org.uk>
+Reported-by: Naresh Madhusudana <naresh.madhusudana@arm.com>
+Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
+Reviewed-by: Matthew Wilcox <mawilcox@microsoft.com>
+Signed-off-by: Will Deacon <will.deacon@arm.com>
+Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
+Acked-by: NeilBrown <neilb@suse.com>
+
+---
+ fs/dcache.c | 8 +++++++-
+ 1 file changed, 7 insertions(+), 1 deletion(-)
+
+--- a/fs/dcache.c
++++ b/fs/dcache.c
+@@ -2464,7 +2464,7 @@ struct dentry *d_alloc_parallel(struct d
+
+ retry:
+ rcu_read_lock();
+- seq = smp_load_acquire(&parent->d_inode->i_dir_seq) & ~1;
++ seq = smp_load_acquire(&parent->d_inode->i_dir_seq);
+ r_seq = read_seqbegin(&rename_lock);
+ dentry = __d_lookup_rcu(parent, name, &d_seq);
+ if (unlikely(dentry)) {
+@@ -2485,6 +2485,12 @@ retry:
+ rcu_read_unlock();
+ goto retry;
+ }
++
++ if (unlikely(seq & 1)) {
++ rcu_read_unlock();
++ goto retry;
++ }
++
+ hlist_bl_lock(b);
+ if (unlikely(parent->d_inode->i_dir_seq != seq)) {
+ hlist_bl_unlock(b);
diff --git a/series.conf b/series.conf
index 58c034c86d..8828dc9a26 100644
--- a/series.conf
+++ b/series.conf
@@ -13794,6 +13794,7 @@
patches.drivers/ASoC-sgtl5000-Fix-suspend-resume
patches.drivers/ASoC-wm_adsp-For-TLV-controls-only-register-TLV-get-
patches.fixes/lock_parent-needs-to-recheck-if-dentry-got-__dentry_.patch
+ patches.fixes/fs-dcache-Avoid-livelock-between-d_alloc_parallel-an.patch
patches.fixes/0005-fs-Teach-path_connected-to-handle-nfs-filesystems-wi.patch
patches.drivers/drm-amdgpu-fix-prime-teardown-order
patches.drivers/drm-radeon-fix-prime-teardown-order