diff options
Diffstat (limited to 'fs/dcache.c')
-rw-r--r-- | fs/dcache.c | 94 |
1 files changed, 74 insertions, 20 deletions
diff --git a/fs/dcache.c b/fs/dcache.c index ea2de7c19b08..59fcffcbf096 100644 --- a/fs/dcache.c +++ b/fs/dcache.c | |||
@@ -1987,28 +1987,36 @@ EXPORT_SYMBOL(d_obtain_root); | |||
1987 | struct dentry *d_add_ci(struct dentry *dentry, struct inode *inode, | 1987 | struct dentry *d_add_ci(struct dentry *dentry, struct inode *inode, |
1988 | struct qstr *name) | 1988 | struct qstr *name) |
1989 | { | 1989 | { |
1990 | struct dentry *found; | 1990 | struct dentry *found, *res; |
1991 | struct dentry *new; | ||
1992 | 1991 | ||
1993 | /* | 1992 | /* |
1994 | * First check if a dentry matching the name already exists, | 1993 | * First check if a dentry matching the name already exists, |
1995 | * if not go ahead and create it now. | 1994 | * if not go ahead and create it now. |
1996 | */ | 1995 | */ |
1997 | found = d_hash_and_lookup(dentry->d_parent, name); | 1996 | found = d_hash_and_lookup(dentry->d_parent, name); |
1998 | if (!found) { | 1997 | if (found) { |
1999 | new = d_alloc(dentry->d_parent, name); | 1998 | iput(inode); |
2000 | if (!new) { | 1999 | return found; |
2001 | found = ERR_PTR(-ENOMEM); | 2000 | } |
2002 | } else { | 2001 | if (d_in_lookup(dentry)) { |
2003 | found = d_splice_alias(inode, new); | 2002 | found = d_alloc_parallel(dentry->d_parent, name, |
2004 | if (found) { | 2003 | dentry->d_wait); |
2005 | dput(new); | 2004 | if (IS_ERR(found) || !d_in_lookup(found)) { |
2006 | return found; | 2005 | iput(inode); |
2007 | } | 2006 | return found; |
2008 | return new; | ||
2009 | } | 2007 | } |
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; | ||
2010 | } | 2019 | } |
2011 | iput(inode); | ||
2012 | return found; | 2020 | return found; |
2013 | } | 2021 | } |
2014 | EXPORT_SYMBOL(d_add_ci); | 2022 | EXPORT_SYMBOL(d_add_ci); |
@@ -2391,8 +2399,23 @@ static inline void end_dir_add(struct inode *dir, unsigned n) | |||
2391 | smp_store_release(&dir->i_dir_seq, n + 2); | 2399 | smp_store_release(&dir->i_dir_seq, n + 2); |
2392 | } | 2400 | } |
2393 | 2401 | ||
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 | |||
2394 | struct dentry *d_alloc_parallel(struct dentry *parent, | 2416 | struct dentry *d_alloc_parallel(struct dentry *parent, |
2395 | const struct qstr *name) | 2417 | const struct qstr *name, |
2418 | wait_queue_head_t *wq) | ||
2396 | { | 2419 | { |
2397 | unsigned int len = name->len; | 2420 | unsigned int len = name->len; |
2398 | unsigned int hash = name->hash; | 2421 | unsigned int hash = name->hash; |
@@ -2463,18 +2486,47 @@ retry: | |||
2463 | } | 2486 | } |
2464 | dget(dentry); | 2487 | dget(dentry); |
2465 | hlist_bl_unlock(b); | 2488 | hlist_bl_unlock(b); |
2466 | /* impossible until we actually enable parallel lookups */ | 2489 | /* somebody is doing lookup for it right now; wait for it */ |
2467 | BUG(); | 2490 | spin_lock(&dentry->d_lock); |
2468 | /* and this will be "wait for it to stop being in-lookup" */ | 2491 | d_wait_lookup(dentry); |
2469 | /* this one will be handled in the next commit */ | 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); | ||
2470 | dput(new); | 2517 | dput(new); |
2471 | return dentry; | 2518 | return dentry; |
2472 | } | 2519 | } |
2473 | /* we can't take ->d_lock here; it's OK, though. */ | 2520 | /* we can't take ->d_lock here; it's OK, though. */ |
2474 | new->d_flags |= DCACHE_PAR_LOOKUP; | 2521 | new->d_flags |= DCACHE_PAR_LOOKUP; |
2522 | new->d_wait = wq; | ||
2475 | hlist_bl_add_head_rcu(&new->d_u.d_in_lookup_hash, b); | 2523 | hlist_bl_add_head_rcu(&new->d_u.d_in_lookup_hash, b); |
2476 | hlist_bl_unlock(b); | 2524 | hlist_bl_unlock(b); |
2477 | return new; | 2525 | return new; |
2526 | mismatch: | ||
2527 | spin_unlock(&dentry->d_lock); | ||
2528 | dput(dentry); | ||
2529 | goto retry; | ||
2478 | } | 2530 | } |
2479 | EXPORT_SYMBOL(d_alloc_parallel); | 2531 | EXPORT_SYMBOL(d_alloc_parallel); |
2480 | 2532 | ||
@@ -2485,9 +2537,11 @@ void __d_lookup_done(struct dentry *dentry) | |||
2485 | hlist_bl_lock(b); | 2537 | hlist_bl_lock(b); |
2486 | dentry->d_flags &= ~DCACHE_PAR_LOOKUP; | 2538 | dentry->d_flags &= ~DCACHE_PAR_LOOKUP; |
2487 | __hlist_bl_del(&dentry->d_u.d_in_lookup_hash); | 2539 | __hlist_bl_del(&dentry->d_u.d_in_lookup_hash); |
2540 | wake_up_all(dentry->d_wait); | ||
2541 | dentry->d_wait = NULL; | ||
2488 | hlist_bl_unlock(b); | 2542 | hlist_bl_unlock(b); |
2489 | INIT_HLIST_NODE(&dentry->d_u.d_alias); | 2543 | INIT_HLIST_NODE(&dentry->d_u.d_alias); |
2490 | /* more stuff will land here */ | 2544 | INIT_LIST_HEAD(&dentry->d_lru); |
2491 | } | 2545 | } |
2492 | EXPORT_SYMBOL(__d_lookup_done); | 2546 | EXPORT_SYMBOL(__d_lookup_done); |
2493 | 2547 | ||