Commit e7d6ef97 authored by Al Viro's avatar Al Viro

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: default avatar"J. R. Okajima" <hooanon05g@gmail.com>
Wearing-brown-paperbag: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent ea01a184
...@@ -2503,7 +2503,6 @@ struct dentry *d_alloc_parallel(struct dentry *parent, ...@@ -2503,7 +2503,6 @@ struct dentry *d_alloc_parallel(struct dentry *parent,
rcu_read_unlock(); rcu_read_unlock();
goto retry; goto retry;
} }
rcu_read_unlock();
/* /*
* No changes for the parent since the beginning of d_lookup(). * No changes for the parent since the beginning of d_lookup().
* Since all removals from the chain happen with hlist_bl_lock(), * Since all removals from the chain happen with hlist_bl_lock(),
...@@ -2516,8 +2515,6 @@ struct dentry *d_alloc_parallel(struct dentry *parent, ...@@ -2516,8 +2515,6 @@ struct dentry *d_alloc_parallel(struct dentry *parent,
continue; continue;
if (dentry->d_parent != parent) if (dentry->d_parent != parent)
continue; continue;
if (d_unhashed(dentry))
continue;
if (parent->d_flags & DCACHE_OP_COMPARE) { if (parent->d_flags & DCACHE_OP_COMPARE) {
int tlen = dentry->d_name.len; int tlen = dentry->d_name.len;
const char *tname = dentry->d_name.name; const char *tname = dentry->d_name.name;
...@@ -2529,9 +2526,18 @@ struct dentry *d_alloc_parallel(struct dentry *parent, ...@@ -2529,9 +2526,18 @@ struct dentry *d_alloc_parallel(struct dentry *parent,
if (dentry_cmp(dentry, str, len)) if (dentry_cmp(dentry, str, len))
continue; continue;
} }
dget(dentry);
hlist_bl_unlock(b); hlist_bl_unlock(b);
/* somebody is doing lookup for it right now; wait for it */ /* now we can try to grab a reference */
if (!lockref_get_not_dead(&dentry->d_lockref)) {
rcu_read_unlock();
goto retry;
}
rcu_read_unlock();
/*
* somebody is likely to be still doing lookup for it;
* wait for them to finish
*/
spin_lock(&dentry->d_lock); spin_lock(&dentry->d_lock);
d_wait_lookup(dentry); d_wait_lookup(dentry);
/* /*
...@@ -2562,6 +2568,7 @@ struct dentry *d_alloc_parallel(struct dentry *parent, ...@@ -2562,6 +2568,7 @@ struct dentry *d_alloc_parallel(struct dentry *parent,
dput(new); dput(new);
return dentry; return dentry;
} }
rcu_read_unlock();
/* we can't take ->d_lock here; it's OK, though. */ /* we can't take ->d_lock here; it's OK, though. */
new->d_flags |= DCACHE_PAR_LOOKUP; new->d_flags |= DCACHE_PAR_LOOKUP;
new->d_wait = wq; new->d_wait = wq;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment