aboutsummaryrefslogtreecommitdiffstats
path: root/fs/dcache.c
diff options
context:
space:
mode:
authorAl Viro <viro@zeniv.linux.org.uk>2016-06-20 01:35:59 -0400
committerAl Viro <viro@zeniv.linux.org.uk>2016-06-20 10:07:42 -0400
commite7d6ef9790bc281f5c29d0132b68031248523fe8 (patch)
tree28ce8447fcc8269544d576de196df8efa335abd2 /fs/dcache.c
parentea01a18494b3d7a91b2f1f2a6a5aaef4741bc294 (diff)
fix idiotic braino in d_alloc_parallel()
Check for d_unhashed() while searching in in-lookup hash was absolutely wrong. Worse, it masked a deadlock on dget() done under bitlock that nests inside ->d_lock. Thanks to J. R. Okajima for spotting it. Spotted-by: "J. R. Okajima" <hooanon05g@gmail.com> Wearing-brown-paperbag: Al Viro <viro@zeniv.linux.org.uk> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'fs/dcache.c')
-rw-r--r--fs/dcache.c17
1 files changed, 12 insertions, 5 deletions
diff --git a/fs/dcache.c b/fs/dcache.c
index b7eddfd35aa5..d6847d7b123d 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -2503,7 +2503,6 @@ retry:
2503 rcu_read_unlock(); 2503 rcu_read_unlock();
2504 goto retry; 2504 goto retry;
2505 } 2505 }
2506 rcu_read_unlock();
2507 /* 2506 /*
2508 * No changes for the parent since the beginning of d_lookup(). 2507 * No changes for the parent since the beginning of d_lookup().
2509 * Since all removals from the chain happen with hlist_bl_lock(), 2508 * Since all removals from the chain happen with hlist_bl_lock(),
@@ -2516,8 +2515,6 @@ retry:
2516 continue; 2515 continue;
2517 if (dentry->d_parent != parent) 2516 if (dentry->d_parent != parent)
2518 continue; 2517 continue;
2519 if (d_unhashed(dentry))
2520 continue;
2521 if (parent->d_flags & DCACHE_OP_COMPARE) { 2518 if (parent->d_flags & DCACHE_OP_COMPARE) {
2522 int tlen = dentry->d_name.len; 2519 int tlen = dentry->d_name.len;
2523 const char *tname = dentry->d_name.name; 2520 const char *tname = dentry->d_name.name;
@@ -2529,9 +2526,18 @@ retry:
2529 if (dentry_cmp(dentry, str, len)) 2526 if (dentry_cmp(dentry, str, len))
2530 continue; 2527 continue;
2531 } 2528 }
2532 dget(dentry);
2533 hlist_bl_unlock(b); 2529 hlist_bl_unlock(b);
2534 /* somebody is doing lookup for it right now; wait for it */ 2530 /* now we can try to grab a reference */
2531 if (!lockref_get_not_dead(&dentry->d_lockref)) {
2532 rcu_read_unlock();
2533 goto retry;
2534 }
2535
2536 rcu_read_unlock();
2537 /*
2538 * somebody is likely to be still doing lookup for it;
2539 * wait for them to finish
2540 */
2535 spin_lock(&dentry->d_lock); 2541 spin_lock(&dentry->d_lock);
2536 d_wait_lookup(dentry); 2542 d_wait_lookup(dentry);
2537 /* 2543 /*
@@ -2562,6 +2568,7 @@ retry:
2562 dput(new); 2568 dput(new);
2563 return dentry; 2569 return dentry;
2564 } 2570 }
2571 rcu_read_unlock();
2565 /* we can't take ->d_lock here; it's OK, though. */ 2572 /* we can't take ->d_lock here; it's OK, though. */
2566 new->d_flags |= DCACHE_PAR_LOOKUP; 2573 new->d_flags |= DCACHE_PAR_LOOKUP;
2567 new->d_wait = wq; 2574 new->d_wait = wq;