diff options
author | Nick Piggin <npiggin@kernel.dk> | 2011-01-07 01:49:33 -0500 |
---|---|---|
committer | Nick Piggin <npiggin@kernel.dk> | 2011-01-07 01:50:21 -0500 |
commit | da5029563a0a026c64821b09e8e7b4fd81d3fe1b (patch) | |
tree | 5d5618e0cb382390073377b1be7d0aa76879ac54 /fs/dcache.c | |
parent | b7ab39f631f505edc2bbdb86620d5493f995c9da (diff) |
fs: dcache scale d_unhashed
Protect d_unhashed(dentry) condition with d_lock. This means keeping
DCACHE_UNHASHED bit in synch with hash manipulations.
Signed-off-by: Nick Piggin <npiggin@kernel.dk>
Diffstat (limited to 'fs/dcache.c')
-rw-r--r-- | fs/dcache.c | 74 |
1 files changed, 50 insertions, 24 deletions
diff --git a/fs/dcache.c b/fs/dcache.c index 81e91502b294..ee127f9ab274 100644 --- a/fs/dcache.c +++ b/fs/dcache.c | |||
@@ -46,6 +46,7 @@ | |||
46 | * - d_name | 46 | * - d_name |
47 | * - d_lru | 47 | * - d_lru |
48 | * - d_count | 48 | * - d_count |
49 | * - d_unhashed() | ||
49 | * | 50 | * |
50 | * Ordering: | 51 | * Ordering: |
51 | * dcache_lock | 52 | * dcache_lock |
@@ -53,6 +54,13 @@ | |||
53 | * dcache_lru_lock | 54 | * dcache_lru_lock |
54 | * dcache_hash_lock | 55 | * dcache_hash_lock |
55 | * | 56 | * |
57 | * If there is an ancestor relationship: | ||
58 | * dentry->d_parent->...->d_parent->d_lock | ||
59 | * ... | ||
60 | * dentry->d_parent->d_lock | ||
61 | * dentry->d_lock | ||
62 | * | ||
63 | * If no ancestor relationship: | ||
56 | * if (dentry1 < dentry2) | 64 | * if (dentry1 < dentry2) |
57 | * dentry1->d_lock | 65 | * dentry1->d_lock |
58 | * dentry2->d_lock | 66 | * dentry2->d_lock |
@@ -379,7 +387,9 @@ int d_invalidate(struct dentry * dentry) | |||
379 | * If it's already been dropped, return OK. | 387 | * If it's already been dropped, return OK. |
380 | */ | 388 | */ |
381 | spin_lock(&dcache_lock); | 389 | spin_lock(&dcache_lock); |
390 | spin_lock(&dentry->d_lock); | ||
382 | if (d_unhashed(dentry)) { | 391 | if (d_unhashed(dentry)) { |
392 | spin_unlock(&dentry->d_lock); | ||
383 | spin_unlock(&dcache_lock); | 393 | spin_unlock(&dcache_lock); |
384 | return 0; | 394 | return 0; |
385 | } | 395 | } |
@@ -388,9 +398,11 @@ int d_invalidate(struct dentry * dentry) | |||
388 | * to get rid of unused child entries. | 398 | * to get rid of unused child entries. |
389 | */ | 399 | */ |
390 | if (!list_empty(&dentry->d_subdirs)) { | 400 | if (!list_empty(&dentry->d_subdirs)) { |
401 | spin_unlock(&dentry->d_lock); | ||
391 | spin_unlock(&dcache_lock); | 402 | spin_unlock(&dcache_lock); |
392 | shrink_dcache_parent(dentry); | 403 | shrink_dcache_parent(dentry); |
393 | spin_lock(&dcache_lock); | 404 | spin_lock(&dcache_lock); |
405 | spin_lock(&dentry->d_lock); | ||
394 | } | 406 | } |
395 | 407 | ||
396 | /* | 408 | /* |
@@ -403,7 +415,6 @@ int d_invalidate(struct dentry * dentry) | |||
403 | * we might still populate it if it was a | 415 | * we might still populate it if it was a |
404 | * working directory or similar). | 416 | * working directory or similar). |
405 | */ | 417 | */ |
406 | spin_lock(&dentry->d_lock); | ||
407 | if (dentry->d_count > 1) { | 418 | if (dentry->d_count > 1) { |
408 | if (dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode)) { | 419 | if (dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode)) { |
409 | spin_unlock(&dentry->d_lock); | 420 | spin_unlock(&dentry->d_lock); |
@@ -490,35 +501,44 @@ EXPORT_SYMBOL(dget_parent); | |||
490 | * any other hashed alias over that one unless @want_discon is set, | 501 | * any other hashed alias over that one unless @want_discon is set, |
491 | * in which case only return an IS_ROOT, DCACHE_DISCONNECTED alias. | 502 | * in which case only return an IS_ROOT, DCACHE_DISCONNECTED alias. |
492 | */ | 503 | */ |
493 | 504 | static struct dentry *__d_find_alias(struct inode *inode, int want_discon) | |
494 | static struct dentry * __d_find_alias(struct inode *inode, int want_discon) | ||
495 | { | 505 | { |
496 | struct list_head *head, *next, *tmp; | 506 | struct dentry *alias, *discon_alias; |
497 | struct dentry *alias, *discon_alias=NULL; | ||
498 | 507 | ||
499 | head = &inode->i_dentry; | 508 | again: |
500 | next = inode->i_dentry.next; | 509 | discon_alias = NULL; |
501 | while (next != head) { | 510 | list_for_each_entry(alias, &inode->i_dentry, d_alias) { |
502 | tmp = next; | 511 | spin_lock(&alias->d_lock); |
503 | next = tmp->next; | ||
504 | prefetch(next); | ||
505 | alias = list_entry(tmp, struct dentry, d_alias); | ||
506 | if (S_ISDIR(inode->i_mode) || !d_unhashed(alias)) { | 512 | if (S_ISDIR(inode->i_mode) || !d_unhashed(alias)) { |
507 | if (IS_ROOT(alias) && | 513 | if (IS_ROOT(alias) && |
508 | (alias->d_flags & DCACHE_DISCONNECTED)) | 514 | (alias->d_flags & DCACHE_DISCONNECTED)) { |
509 | discon_alias = alias; | 515 | discon_alias = alias; |
510 | else if (!want_discon) { | 516 | } else if (!want_discon) { |
511 | __dget_locked(alias); | 517 | __dget_locked_dlock(alias); |
518 | spin_unlock(&alias->d_lock); | ||
519 | return alias; | ||
520 | } | ||
521 | } | ||
522 | spin_unlock(&alias->d_lock); | ||
523 | } | ||
524 | if (discon_alias) { | ||
525 | alias = discon_alias; | ||
526 | spin_lock(&alias->d_lock); | ||
527 | if (S_ISDIR(inode->i_mode) || !d_unhashed(alias)) { | ||
528 | if (IS_ROOT(alias) && | ||
529 | (alias->d_flags & DCACHE_DISCONNECTED)) { | ||
530 | __dget_locked_dlock(alias); | ||
531 | spin_unlock(&alias->d_lock); | ||
512 | return alias; | 532 | return alias; |
513 | } | 533 | } |
514 | } | 534 | } |
535 | spin_unlock(&alias->d_lock); | ||
536 | goto again; | ||
515 | } | 537 | } |
516 | if (discon_alias) | 538 | return NULL; |
517 | __dget_locked(discon_alias); | ||
518 | return discon_alias; | ||
519 | } | 539 | } |
520 | 540 | ||
521 | struct dentry * d_find_alias(struct inode *inode) | 541 | struct dentry *d_find_alias(struct inode *inode) |
522 | { | 542 | { |
523 | struct dentry *de = NULL; | 543 | struct dentry *de = NULL; |
524 | 544 | ||
@@ -801,8 +821,8 @@ static void shrink_dcache_for_umount_subtree(struct dentry *dentry) | |||
801 | spin_lock(&dcache_lock); | 821 | spin_lock(&dcache_lock); |
802 | spin_lock(&dentry->d_lock); | 822 | spin_lock(&dentry->d_lock); |
803 | dentry_lru_del(dentry); | 823 | dentry_lru_del(dentry); |
804 | spin_unlock(&dentry->d_lock); | ||
805 | __d_drop(dentry); | 824 | __d_drop(dentry); |
825 | spin_unlock(&dentry->d_lock); | ||
806 | spin_unlock(&dcache_lock); | 826 | spin_unlock(&dcache_lock); |
807 | 827 | ||
808 | for (;;) { | 828 | for (;;) { |
@@ -817,8 +837,8 @@ static void shrink_dcache_for_umount_subtree(struct dentry *dentry) | |||
817 | d_u.d_child) { | 837 | d_u.d_child) { |
818 | spin_lock(&loop->d_lock); | 838 | spin_lock(&loop->d_lock); |
819 | dentry_lru_del(loop); | 839 | dentry_lru_del(loop); |
820 | spin_unlock(&loop->d_lock); | ||
821 | __d_drop(loop); | 840 | __d_drop(loop); |
841 | spin_unlock(&loop->d_lock); | ||
822 | cond_resched_lock(&dcache_lock); | 842 | cond_resched_lock(&dcache_lock); |
823 | } | 843 | } |
824 | spin_unlock(&dcache_lock); | 844 | spin_unlock(&dcache_lock); |
@@ -1863,7 +1883,10 @@ static void d_move_locked(struct dentry * dentry, struct dentry * target) | |||
1863 | /* | 1883 | /* |
1864 | * XXXX: do we really need to take target->d_lock? | 1884 | * XXXX: do we really need to take target->d_lock? |
1865 | */ | 1885 | */ |
1866 | if (target < dentry) { | 1886 | if (d_ancestor(dentry, target)) { |
1887 | spin_lock(&dentry->d_lock); | ||
1888 | spin_lock_nested(&target->d_lock, DENTRY_D_LOCK_NESTED); | ||
1889 | } else if (d_ancestor(target, dentry) || target < dentry) { | ||
1867 | spin_lock(&target->d_lock); | 1890 | spin_lock(&target->d_lock); |
1868 | spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED); | 1891 | spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED); |
1869 | } else { | 1892 | } else { |
@@ -2542,13 +2565,16 @@ resume: | |||
2542 | struct list_head *tmp = next; | 2565 | struct list_head *tmp = next; |
2543 | struct dentry *dentry = list_entry(tmp, struct dentry, d_u.d_child); | 2566 | struct dentry *dentry = list_entry(tmp, struct dentry, d_u.d_child); |
2544 | next = tmp->next; | 2567 | next = tmp->next; |
2545 | if (d_unhashed(dentry)||!dentry->d_inode) | 2568 | spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED); |
2569 | if (d_unhashed(dentry) || !dentry->d_inode) { | ||
2570 | spin_unlock(&dentry->d_lock); | ||
2546 | continue; | 2571 | continue; |
2572 | } | ||
2547 | if (!list_empty(&dentry->d_subdirs)) { | 2573 | if (!list_empty(&dentry->d_subdirs)) { |
2574 | spin_unlock(&dentry->d_lock); | ||
2548 | this_parent = dentry; | 2575 | this_parent = dentry; |
2549 | goto repeat; | 2576 | goto repeat; |
2550 | } | 2577 | } |
2551 | spin_lock(&dentry->d_lock); | ||
2552 | dentry->d_count--; | 2578 | dentry->d_count--; |
2553 | spin_unlock(&dentry->d_lock); | 2579 | spin_unlock(&dentry->d_lock); |
2554 | } | 2580 | } |