Skip to content

Commit d9171b9

Browse files
author
Al Viro
committed
parallel lookups machinery, part 4 (and last)
If we *do* run into an in-lookup match, we need to wait for it to cease being in-lookup. Fortunately, we do have unused space in in-lookup dentries - d_lru is never looked at until it stops being in-lookup. So we can stash a pointer to wait_queue_head from stack frame of the caller of ->lookup(). Some precautions are needed while waiting, but it's not that hard - we do hold a reference to dentry we are waiting for, so it can't go away. If it's found to be in-lookup the wait_queue_head is still alive and will remain so at least while ->d_lock is held. Moreover, the condition we are waiting for becomes true at the same point where everything on that wq gets woken up, so we can just add ourselves to the queue once. d_alloc_parallel() gets a pointer to wait_queue_head_t from its caller; lookup_slow() adjusted, d_add_ci() taught to use d_alloc_parallel() if the dentry passed to it happens to be in-lookup one (i.e. if it's been called from the parallel lookup). That's pretty much it - all that remains is to switch ->i_mutex to rwsem and have lookup_slow() take it shared. Signed-off-by: Al Viro <[email protected]>
1 parent 94bdd65 commit d9171b9

File tree

3 files changed

+82
-23
lines changed

3 files changed

+82
-23
lines changed

fs/dcache.c

Lines changed: 74 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1987,28 +1987,36 @@ EXPORT_SYMBOL(d_obtain_root);
19871987
struct dentry *d_add_ci(struct dentry *dentry, struct inode *inode,
19881988
struct qstr *name)
19891989
{
1990-
struct dentry *found;
1991-
struct dentry *new;
1990+
struct dentry *found, *res;
19921991

19931992
/*
19941993
* First check if a dentry matching the name already exists,
19951994
* if not go ahead and create it now.
19961995
*/
19971996
found = d_hash_and_lookup(dentry->d_parent, name);
1998-
if (!found) {
1999-
new = d_alloc(dentry->d_parent, name);
2000-
if (!new) {
2001-
found = ERR_PTR(-ENOMEM);
2002-
} else {
2003-
found = d_splice_alias(inode, new);
2004-
if (found) {
2005-
dput(new);
2006-
return found;
2007-
}
2008-
return new;
1997+
if (found) {
1998+
iput(inode);
1999+
return found;
2000+
}
2001+
if (d_in_lookup(dentry)) {
2002+
found = d_alloc_parallel(dentry->d_parent, name,
2003+
dentry->d_wait);
2004+
if (IS_ERR(found) || !d_in_lookup(found)) {
2005+
iput(inode);
2006+
return found;
20092007
}
2008+
} else {
2009+
found = d_alloc(dentry->d_parent, name);
2010+
if (!found) {
2011+
iput(inode);
2012+
return ERR_PTR(-ENOMEM);
2013+
}
2014+
}
2015+
res = d_splice_alias(inode, found);
2016+
if (res) {
2017+
dput(found);
2018+
return res;
20102019
}
2011-
iput(inode);
20122020
return found;
20132021
}
20142022
EXPORT_SYMBOL(d_add_ci);
@@ -2391,8 +2399,23 @@ static inline void end_dir_add(struct inode *dir, unsigned n)
23912399
smp_store_release(&dir->i_dir_seq, n + 2);
23922400
}
23932401

2402+
static void d_wait_lookup(struct dentry *dentry)
2403+
{
2404+
if (d_in_lookup(dentry)) {
2405+
DECLARE_WAITQUEUE(wait, current);
2406+
add_wait_queue(dentry->d_wait, &wait);
2407+
do {
2408+
set_current_state(TASK_UNINTERRUPTIBLE);
2409+
spin_unlock(&dentry->d_lock);
2410+
schedule();
2411+
spin_lock(&dentry->d_lock);
2412+
} while (d_in_lookup(dentry));
2413+
}
2414+
}
2415+
23942416
struct dentry *d_alloc_parallel(struct dentry *parent,
2395-
const struct qstr *name)
2417+
const struct qstr *name,
2418+
wait_queue_head_t *wq)
23962419
{
23972420
unsigned int len = name->len;
23982421
unsigned int hash = name->hash;
@@ -2463,18 +2486,47 @@ struct dentry *d_alloc_parallel(struct dentry *parent,
24632486
}
24642487
dget(dentry);
24652488
hlist_bl_unlock(b);
2466-
/* impossible until we actually enable parallel lookups */
2467-
BUG();
2468-
/* and this will be "wait for it to stop being in-lookup" */
2469-
/* this one will be handled in the next commit */
2489+
/* somebody is doing lookup for it right now; wait for it */
2490+
spin_lock(&dentry->d_lock);
2491+
d_wait_lookup(dentry);
2492+
/*
2493+
* it's not in-lookup anymore; in principle we should repeat
2494+
* everything from dcache lookup, but it's likely to be what
2495+
* d_lookup() would've found anyway. If it is, just return it;
2496+
* otherwise we really have to repeat the whole thing.
2497+
*/
2498+
if (unlikely(dentry->d_name.hash != hash))
2499+
goto mismatch;
2500+
if (unlikely(dentry->d_parent != parent))
2501+
goto mismatch;
2502+
if (unlikely(d_unhashed(dentry)))
2503+
goto mismatch;
2504+
if (parent->d_flags & DCACHE_OP_COMPARE) {
2505+
int tlen = dentry->d_name.len;
2506+
const char *tname = dentry->d_name.name;
2507+
if (parent->d_op->d_compare(parent, dentry, tlen, tname, name))
2508+
goto mismatch;
2509+
} else {
2510+
if (unlikely(dentry->d_name.len != len))
2511+
goto mismatch;
2512+
if (unlikely(dentry_cmp(dentry, str, len)))
2513+
goto mismatch;
2514+
}
2515+
/* OK, it *is* a hashed match; return it */
2516+
spin_unlock(&dentry->d_lock);
24702517
dput(new);
24712518
return dentry;
24722519
}
24732520
/* we can't take ->d_lock here; it's OK, though. */
24742521
new->d_flags |= DCACHE_PAR_LOOKUP;
2522+
new->d_wait = wq;
24752523
hlist_bl_add_head_rcu(&new->d_u.d_in_lookup_hash, b);
24762524
hlist_bl_unlock(b);
24772525
return new;
2526+
mismatch:
2527+
spin_unlock(&dentry->d_lock);
2528+
dput(dentry);
2529+
goto retry;
24782530
}
24792531
EXPORT_SYMBOL(d_alloc_parallel);
24802532

@@ -2485,9 +2537,11 @@ void __d_lookup_done(struct dentry *dentry)
24852537
hlist_bl_lock(b);
24862538
dentry->d_flags &= ~DCACHE_PAR_LOOKUP;
24872539
__hlist_bl_del(&dentry->d_u.d_in_lookup_hash);
2540+
wake_up_all(dentry->d_wait);
2541+
dentry->d_wait = NULL;
24882542
hlist_bl_unlock(b);
24892543
INIT_HLIST_NODE(&dentry->d_u.d_alias);
2490-
/* more stuff will land here */
2544+
INIT_LIST_HEAD(&dentry->d_lru);
24912545
}
24922546
EXPORT_SYMBOL(__d_lookup_done);
24932547

fs/namei.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1605,13 +1605,14 @@ static struct dentry *lookup_slow(const struct qstr *name,
16051605
{
16061606
struct dentry *dentry = ERR_PTR(-ENOENT), *old;
16071607
struct inode *inode = dir->d_inode;
1608+
DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
16081609

16091610
inode_lock(inode);
16101611
/* Don't go there if it's already dead */
16111612
if (unlikely(IS_DEADDIR(inode)))
16121613
goto out;
16131614
again:
1614-
dentry = d_alloc_parallel(dir, name);
1615+
dentry = d_alloc_parallel(dir, name, &wq);
16151616
if (IS_ERR(dentry))
16161617
goto out;
16171618
if (unlikely(!d_in_lookup(dentry))) {

include/linux/dcache.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,10 @@ struct dentry {
123123
unsigned long d_time; /* used by d_revalidate */
124124
void *d_fsdata; /* fs-specific data */
125125

126-
struct list_head d_lru; /* LRU list */
126+
union {
127+
struct list_head d_lru; /* LRU list */
128+
wait_queue_head_t *d_wait; /* in-lookup ones only */
129+
};
127130
struct list_head d_child; /* child of parent list */
128131
struct list_head d_subdirs; /* our children */
129132
/*
@@ -251,7 +254,8 @@ extern void d_set_d_op(struct dentry *dentry, const struct dentry_operations *op
251254
/* allocate/de-allocate */
252255
extern struct dentry * d_alloc(struct dentry *, const struct qstr *);
253256
extern struct dentry * d_alloc_pseudo(struct super_block *, const struct qstr *);
254-
extern struct dentry * d_alloc_parallel(struct dentry *, const struct qstr *);
257+
extern struct dentry * d_alloc_parallel(struct dentry *, const struct qstr *,
258+
wait_queue_head_t *);
255259
extern struct dentry * d_splice_alias(struct inode *, struct dentry *);
256260
extern struct dentry * d_add_ci(struct dentry *, struct inode *, struct qstr *);
257261
extern struct dentry * d_exact_alias(struct dentry *, struct inode *);

0 commit comments

Comments
 (0)