diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2016-06-20 01:35:59 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2016-06-20 10:07:42 -0400 |
commit | e7d6ef9790bc281f5c29d0132b68031248523fe8 (patch) | |
tree | 28ce8447fcc8269544d576de196df8efa335abd2 /fs/dcache.c | |
parent | ea01a18494b3d7a91b2f1f2a6a5aaef4741bc294 (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.c | 17 |
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; |