diff options
Diffstat (limited to 'fs/dcache.c')
-rw-r--r-- | fs/dcache.c | 75 |
1 files changed, 64 insertions, 11 deletions
diff --git a/fs/dcache.c b/fs/dcache.c index 817c243c1ff1..d6847d7b123d 100644 --- a/fs/dcache.c +++ b/fs/dcache.c | |||
@@ -507,6 +507,44 @@ void d_drop(struct dentry *dentry) | |||
507 | } | 507 | } |
508 | EXPORT_SYMBOL(d_drop); | 508 | EXPORT_SYMBOL(d_drop); |
509 | 509 | ||
510 | static inline void dentry_unlist(struct dentry *dentry, struct dentry *parent) | ||
511 | { | ||
512 | struct dentry *next; | ||
513 | /* | ||
514 | * Inform d_walk() and shrink_dentry_list() that we are no longer | ||
515 | * attached to the dentry tree | ||
516 | */ | ||
517 | dentry->d_flags |= DCACHE_DENTRY_KILLED; | ||
518 | if (unlikely(list_empty(&dentry->d_child))) | ||
519 | return; | ||
520 | __list_del_entry(&dentry->d_child); | ||
521 | /* | ||
522 | * Cursors can move around the list of children. While we'd been | ||
523 | * a normal list member, it didn't matter - ->d_child.next would've | ||
524 | * been updated. However, from now on it won't be and for the | ||
525 | * things like d_walk() it might end up with a nasty surprise. | ||
526 | * Normally d_walk() doesn't care about cursors moving around - | ||
527 | * ->d_lock on parent prevents that and since a cursor has no children | ||
528 | * of its own, we get through it without ever unlocking the parent. | ||
529 | * There is one exception, though - if we ascend from a child that | ||
530 | * gets killed as soon as we unlock it, the next sibling is found | ||
531 | * using the value left in its ->d_child.next. And if _that_ | ||
532 | * pointed to a cursor, and cursor got moved (e.g. by lseek()) | ||
533 | * before d_walk() regains parent->d_lock, we'll end up skipping | ||
534 | * everything the cursor had been moved past. | ||
535 | * | ||
536 | * Solution: make sure that the pointer left behind in ->d_child.next | ||
537 | * points to something that won't be moving around. I.e. skip the | ||
538 | * cursors. | ||
539 | */ | ||
540 | while (dentry->d_child.next != &parent->d_subdirs) { | ||
541 | next = list_entry(dentry->d_child.next, struct dentry, d_child); | ||
542 | if (likely(!(next->d_flags & DCACHE_DENTRY_CURSOR))) | ||
543 | break; | ||
544 | dentry->d_child.next = next->d_child.next; | ||
545 | } | ||
546 | } | ||
547 | |||
510 | static void __dentry_kill(struct dentry *dentry) | 548 | static void __dentry_kill(struct dentry *dentry) |
511 | { | 549 | { |
512 | struct dentry *parent = NULL; | 550 | struct dentry *parent = NULL; |
@@ -532,12 +570,7 @@ static void __dentry_kill(struct dentry *dentry) | |||
532 | } | 570 | } |
533 | /* if it was on the hash then remove it */ | 571 | /* if it was on the hash then remove it */ |
534 | __d_drop(dentry); | 572 | __d_drop(dentry); |
535 | __list_del_entry(&dentry->d_child); | 573 | dentry_unlist(dentry, parent); |
536 | /* | ||
537 | * Inform d_walk() that we are no longer attached to the | ||
538 | * dentry tree | ||
539 | */ | ||
540 | dentry->d_flags |= DCACHE_DENTRY_KILLED; | ||
541 | if (parent) | 574 | if (parent) |
542 | spin_unlock(&parent->d_lock); | 575 | spin_unlock(&parent->d_lock); |
543 | dentry_iput(dentry); | 576 | dentry_iput(dentry); |
@@ -1203,6 +1236,9 @@ resume: | |||
1203 | struct dentry *dentry = list_entry(tmp, struct dentry, d_child); | 1236 | struct dentry *dentry = list_entry(tmp, struct dentry, d_child); |
1204 | next = tmp->next; | 1237 | next = tmp->next; |
1205 | 1238 | ||
1239 | if (unlikely(dentry->d_flags & DCACHE_DENTRY_CURSOR)) | ||
1240 | continue; | ||
1241 | |||
1206 | spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED); | 1242 | spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED); |
1207 | 1243 | ||
1208 | ret = enter(data, dentry); | 1244 | ret = enter(data, dentry); |
@@ -1651,6 +1687,16 @@ struct dentry *d_alloc(struct dentry * parent, const struct qstr *name) | |||
1651 | } | 1687 | } |
1652 | EXPORT_SYMBOL(d_alloc); | 1688 | EXPORT_SYMBOL(d_alloc); |
1653 | 1689 | ||
1690 | struct dentry *d_alloc_cursor(struct dentry * parent) | ||
1691 | { | ||
1692 | struct dentry *dentry = __d_alloc(parent->d_sb, NULL); | ||
1693 | if (dentry) { | ||
1694 | dentry->d_flags |= DCACHE_RCUACCESS | DCACHE_DENTRY_CURSOR; | ||
1695 | dentry->d_parent = dget(parent); | ||
1696 | } | ||
1697 | return dentry; | ||
1698 | } | ||
1699 | |||
1654 | /** | 1700 | /** |
1655 | * d_alloc_pseudo - allocate a dentry (for lookup-less filesystems) | 1701 | * d_alloc_pseudo - allocate a dentry (for lookup-less filesystems) |
1656 | * @sb: the superblock | 1702 | * @sb: the superblock |
@@ -2457,7 +2503,6 @@ retry: | |||
2457 | rcu_read_unlock(); | 2503 | rcu_read_unlock(); |
2458 | goto retry; | 2504 | goto retry; |
2459 | } | 2505 | } |
2460 | rcu_read_unlock(); | ||
2461 | /* | 2506 | /* |
2462 | * No changes for the parent since the beginning of d_lookup(). | 2507 | * No changes for the parent since the beginning of d_lookup(). |
2463 | * Since all removals from the chain happen with hlist_bl_lock(), | 2508 | * Since all removals from the chain happen with hlist_bl_lock(), |
@@ -2470,8 +2515,6 @@ retry: | |||
2470 | continue; | 2515 | continue; |
2471 | if (dentry->d_parent != parent) | 2516 | if (dentry->d_parent != parent) |
2472 | continue; | 2517 | continue; |
2473 | if (d_unhashed(dentry)) | ||
2474 | continue; | ||
2475 | if (parent->d_flags & DCACHE_OP_COMPARE) { | 2518 | if (parent->d_flags & DCACHE_OP_COMPARE) { |
2476 | int tlen = dentry->d_name.len; | 2519 | int tlen = dentry->d_name.len; |
2477 | const char *tname = dentry->d_name.name; | 2520 | const char *tname = dentry->d_name.name; |
@@ -2483,9 +2526,18 @@ retry: | |||
2483 | if (dentry_cmp(dentry, str, len)) | 2526 | if (dentry_cmp(dentry, str, len)) |
2484 | continue; | 2527 | continue; |
2485 | } | 2528 | } |
2486 | dget(dentry); | ||
2487 | hlist_bl_unlock(b); | 2529 | hlist_bl_unlock(b); |
2488 | /* 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 | */ | ||
2489 | spin_lock(&dentry->d_lock); | 2541 | spin_lock(&dentry->d_lock); |
2490 | d_wait_lookup(dentry); | 2542 | d_wait_lookup(dentry); |
2491 | /* | 2543 | /* |
@@ -2516,6 +2568,7 @@ retry: | |||
2516 | dput(new); | 2568 | dput(new); |
2517 | return dentry; | 2569 | return dentry; |
2518 | } | 2570 | } |
2571 | rcu_read_unlock(); | ||
2519 | /* we can't take ->d_lock here; it's OK, though. */ | 2572 | /* we can't take ->d_lock here; it's OK, though. */ |
2520 | new->d_flags |= DCACHE_PAR_LOOKUP; | 2573 | new->d_flags |= DCACHE_PAR_LOOKUP; |
2521 | new->d_wait = wq; | 2574 | new->d_wait = wq; |