diff options
author | Ian Kent <raven@themaw.net> | 2008-07-24 00:30:09 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2008-07-24 13:47:31 -0400 |
commit | 5f6f4f28b6ba543beef8bad91aa6f69c7ffeee51 (patch) | |
tree | 72f3aac56d49c7bea7940f8d8b6e8ca559e3ae1c /fs/autofs4/root.c | |
parent | 391b52f98cf2e9bff227dad8bf9ea206fec43fa4 (diff) |
autofs4: don't make expiring dentry negative
Correct the error of making a positive dentry negative after it has been
instantiated.
The code that makes this error attempts to re-use the dentry from a
concurrent expire and mount to resolve a race and the dentry used for the
lookup must be negative for mounts to trigger in the required cases. The
fact is that the dentry doesn't need to be re-used because all that is
needed is to preserve the flag that indicates an expire is still
incomplete at the time of the mount request.
This change uses the the dentry to check the flag and wait for the expire
to complete then discards it instead of attempting to re-use it.
Signed-off-by: Ian Kent <raven@themaw.net>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs/autofs4/root.c')
-rw-r--r-- | fs/autofs4/root.c | 118 |
1 files changed, 46 insertions, 72 deletions
diff --git a/fs/autofs4/root.c b/fs/autofs4/root.c index edf5b6bddb52..9ead2279df4f 100644 --- a/fs/autofs4/root.c +++ b/fs/autofs4/root.c | |||
@@ -493,10 +493,10 @@ void autofs4_dentry_release(struct dentry *de) | |||
493 | struct autofs_sb_info *sbi = autofs4_sbi(de->d_sb); | 493 | struct autofs_sb_info *sbi = autofs4_sbi(de->d_sb); |
494 | 494 | ||
495 | if (sbi) { | 495 | if (sbi) { |
496 | spin_lock(&sbi->rehash_lock); | 496 | spin_lock(&sbi->lookup_lock); |
497 | if (!list_empty(&inf->rehash)) | 497 | if (!list_empty(&inf->expiring)) |
498 | list_del(&inf->rehash); | 498 | list_del(&inf->expiring); |
499 | spin_unlock(&sbi->rehash_lock); | 499 | spin_unlock(&sbi->lookup_lock); |
500 | } | 500 | } |
501 | 501 | ||
502 | inf->dentry = NULL; | 502 | inf->dentry = NULL; |
@@ -518,7 +518,7 @@ static struct dentry_operations autofs4_dentry_operations = { | |||
518 | .d_release = autofs4_dentry_release, | 518 | .d_release = autofs4_dentry_release, |
519 | }; | 519 | }; |
520 | 520 | ||
521 | static struct dentry *autofs4_lookup_unhashed(struct autofs_sb_info *sbi, struct dentry *parent, struct qstr *name) | 521 | static struct dentry *autofs4_lookup_expiring(struct autofs_sb_info *sbi, struct dentry *parent, struct qstr *name) |
522 | { | 522 | { |
523 | unsigned int len = name->len; | 523 | unsigned int len = name->len; |
524 | unsigned int hash = name->hash; | 524 | unsigned int hash = name->hash; |
@@ -526,14 +526,14 @@ static struct dentry *autofs4_lookup_unhashed(struct autofs_sb_info *sbi, struct | |||
526 | struct list_head *p, *head; | 526 | struct list_head *p, *head; |
527 | 527 | ||
528 | spin_lock(&dcache_lock); | 528 | spin_lock(&dcache_lock); |
529 | spin_lock(&sbi->rehash_lock); | 529 | spin_lock(&sbi->lookup_lock); |
530 | head = &sbi->rehash_list; | 530 | head = &sbi->expiring_list; |
531 | list_for_each(p, head) { | 531 | list_for_each(p, head) { |
532 | struct autofs_info *ino; | 532 | struct autofs_info *ino; |
533 | struct dentry *dentry; | 533 | struct dentry *dentry; |
534 | struct qstr *qstr; | 534 | struct qstr *qstr; |
535 | 535 | ||
536 | ino = list_entry(p, struct autofs_info, rehash); | 536 | ino = list_entry(p, struct autofs_info, expiring); |
537 | dentry = ino->dentry; | 537 | dentry = ino->dentry; |
538 | 538 | ||
539 | spin_lock(&dentry->d_lock); | 539 | spin_lock(&dentry->d_lock); |
@@ -555,33 +555,16 @@ static struct dentry *autofs4_lookup_unhashed(struct autofs_sb_info *sbi, struct | |||
555 | goto next; | 555 | goto next; |
556 | 556 | ||
557 | if (d_unhashed(dentry)) { | 557 | if (d_unhashed(dentry)) { |
558 | struct inode *inode = dentry->d_inode; | ||
559 | |||
560 | ino = autofs4_dentry_ino(dentry); | ||
561 | list_del_init(&ino->rehash); | ||
562 | dget(dentry); | 558 | dget(dentry); |
563 | /* | ||
564 | * Make the rehashed dentry negative so the VFS | ||
565 | * behaves as it should. | ||
566 | */ | ||
567 | if (inode) { | ||
568 | dentry->d_inode = NULL; | ||
569 | list_del_init(&dentry->d_alias); | ||
570 | spin_unlock(&dentry->d_lock); | ||
571 | spin_unlock(&sbi->rehash_lock); | ||
572 | spin_unlock(&dcache_lock); | ||
573 | iput(inode); | ||
574 | return dentry; | ||
575 | } | ||
576 | spin_unlock(&dentry->d_lock); | 559 | spin_unlock(&dentry->d_lock); |
577 | spin_unlock(&sbi->rehash_lock); | 560 | spin_unlock(&sbi->lookup_lock); |
578 | spin_unlock(&dcache_lock); | 561 | spin_unlock(&dcache_lock); |
579 | return dentry; | 562 | return dentry; |
580 | } | 563 | } |
581 | next: | 564 | next: |
582 | spin_unlock(&dentry->d_lock); | 565 | spin_unlock(&dentry->d_lock); |
583 | } | 566 | } |
584 | spin_unlock(&sbi->rehash_lock); | 567 | spin_unlock(&sbi->lookup_lock); |
585 | spin_unlock(&dcache_lock); | 568 | spin_unlock(&dcache_lock); |
586 | 569 | ||
587 | return NULL; | 570 | return NULL; |
@@ -591,7 +574,7 @@ next: | |||
591 | static struct dentry *autofs4_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) | 574 | static struct dentry *autofs4_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) |
592 | { | 575 | { |
593 | struct autofs_sb_info *sbi; | 576 | struct autofs_sb_info *sbi; |
594 | struct dentry *unhashed; | 577 | struct dentry *expiring; |
595 | int oz_mode; | 578 | int oz_mode; |
596 | 579 | ||
597 | DPRINTK("name = %.*s", | 580 | DPRINTK("name = %.*s", |
@@ -607,44 +590,44 @@ static struct dentry *autofs4_lookup(struct inode *dir, struct dentry *dentry, s | |||
607 | DPRINTK("pid = %u, pgrp = %u, catatonic = %d, oz_mode = %d", | 590 | DPRINTK("pid = %u, pgrp = %u, catatonic = %d, oz_mode = %d", |
608 | current->pid, task_pgrp_nr(current), sbi->catatonic, oz_mode); | 591 | current->pid, task_pgrp_nr(current), sbi->catatonic, oz_mode); |
609 | 592 | ||
610 | unhashed = autofs4_lookup_unhashed(sbi, dentry->d_parent, &dentry->d_name); | 593 | expiring = autofs4_lookup_expiring(sbi, dentry->d_parent, &dentry->d_name); |
611 | if (!unhashed) { | 594 | if (expiring) { |
612 | /* | 595 | struct autofs_info *ino = autofs4_dentry_ino(expiring); |
613 | * Mark the dentry incomplete but don't hash it. We do this | ||
614 | * to serialize our inode creation operations (symlink and | ||
615 | * mkdir) which prevents deadlock during the callback to | ||
616 | * the daemon. Subsequent user space lookups for the same | ||
617 | * dentry are placed on the wait queue while the daemon | ||
618 | * itself is allowed passage unresticted so the create | ||
619 | * operation itself can then hash the dentry. Finally, | ||
620 | * we check for the hashed dentry and return the newly | ||
621 | * hashed dentry. | ||
622 | */ | ||
623 | dentry->d_op = &autofs4_root_dentry_operations; | ||
624 | |||
625 | dentry->d_fsdata = NULL; | ||
626 | d_instantiate(dentry, NULL); | ||
627 | } else { | ||
628 | struct autofs_info *ino = autofs4_dentry_ino(unhashed); | ||
629 | DPRINTK("rehash %p with %p", dentry, unhashed); | ||
630 | /* | 596 | /* |
631 | * If we are racing with expire the request might not | 597 | * If we are racing with expire the request might not |
632 | * be quite complete but the directory has been removed | 598 | * be quite complete but the directory has been removed |
633 | * so it must have been successful, so just wait for it. | 599 | * so it must have been successful, so just wait for it. |
634 | * We need to ensure the AUTOFS_INF_EXPIRING flag is clear | ||
635 | * before continuing as revalidate may fail when calling | ||
636 | * try_to_fill_dentry (returning EAGAIN) if we don't. | ||
637 | */ | 600 | */ |
638 | while (ino && (ino->flags & AUTOFS_INF_EXPIRING)) { | 601 | while (ino && (ino->flags & AUTOFS_INF_EXPIRING)) { |
639 | DPRINTK("wait for incomplete expire %p name=%.*s", | 602 | DPRINTK("wait for incomplete expire %p name=%.*s", |
640 | unhashed, unhashed->d_name.len, | 603 | expiring, expiring->d_name.len, |
641 | unhashed->d_name.name); | 604 | expiring->d_name.name); |
642 | autofs4_wait(sbi, unhashed, NFY_NONE); | 605 | autofs4_wait(sbi, expiring, NFY_NONE); |
643 | DPRINTK("request completed"); | 606 | DPRINTK("request completed"); |
644 | } | 607 | } |
645 | dentry = unhashed; | 608 | spin_lock(&sbi->lookup_lock); |
609 | if (!list_empty(&ino->expiring)) | ||
610 | list_del_init(&ino->expiring); | ||
611 | spin_unlock(&sbi->lookup_lock); | ||
612 | dput(expiring); | ||
646 | } | 613 | } |
647 | 614 | ||
615 | /* | ||
616 | * Mark the dentry incomplete but don't hash it. We do this | ||
617 | * to serialize our inode creation operations (symlink and | ||
618 | * mkdir) which prevents deadlock during the callback to | ||
619 | * the daemon. Subsequent user space lookups for the same | ||
620 | * dentry are placed on the wait queue while the daemon | ||
621 | * itself is allowed passage unresticted so the create | ||
622 | * operation itself can then hash the dentry. Finally, | ||
623 | * we check for the hashed dentry and return the newly | ||
624 | * hashed dentry. | ||
625 | */ | ||
626 | dentry->d_op = &autofs4_root_dentry_operations; | ||
627 | |||
628 | dentry->d_fsdata = NULL; | ||
629 | d_instantiate(dentry, NULL); | ||
630 | |||
648 | if (!oz_mode) { | 631 | if (!oz_mode) { |
649 | spin_lock(&dentry->d_lock); | 632 | spin_lock(&dentry->d_lock); |
650 | dentry->d_flags |= DCACHE_AUTOFS_PENDING; | 633 | dentry->d_flags |= DCACHE_AUTOFS_PENDING; |
@@ -668,8 +651,6 @@ static struct dentry *autofs4_lookup(struct inode *dir, struct dentry *dentry, s | |||
668 | if (sigismember (sigset, SIGKILL) || | 651 | if (sigismember (sigset, SIGKILL) || |
669 | sigismember (sigset, SIGQUIT) || | 652 | sigismember (sigset, SIGQUIT) || |
670 | sigismember (sigset, SIGINT)) { | 653 | sigismember (sigset, SIGINT)) { |
671 | if (unhashed) | ||
672 | dput(unhashed); | ||
673 | return ERR_PTR(-ERESTARTNOINTR); | 654 | return ERR_PTR(-ERESTARTNOINTR); |
674 | } | 655 | } |
675 | } | 656 | } |
@@ -699,15 +680,9 @@ static struct dentry *autofs4_lookup(struct inode *dir, struct dentry *dentry, s | |||
699 | else | 680 | else |
700 | dentry = ERR_PTR(-ENOENT); | 681 | dentry = ERR_PTR(-ENOENT); |
701 | 682 | ||
702 | if (unhashed) | ||
703 | dput(unhashed); | ||
704 | |||
705 | return dentry; | 683 | return dentry; |
706 | } | 684 | } |
707 | 685 | ||
708 | if (unhashed) | ||
709 | return dentry; | ||
710 | |||
711 | return NULL; | 686 | return NULL; |
712 | } | 687 | } |
713 | 688 | ||
@@ -769,9 +744,8 @@ static int autofs4_dir_symlink(struct inode *dir, | |||
769 | * that the file no longer exists. However, doing that means that the | 744 | * that the file no longer exists. However, doing that means that the |
770 | * VFS layer can turn the dentry into a negative dentry. We don't want | 745 | * VFS layer can turn the dentry into a negative dentry. We don't want |
771 | * this, because the unlink is probably the result of an expire. | 746 | * this, because the unlink is probably the result of an expire. |
772 | * We simply d_drop it and add it to a rehash candidates list in the | 747 | * We simply d_drop it and add it to a expiring list in the super block, |
773 | * super block, which allows the dentry lookup to reuse it retaining | 748 | * which allows the dentry lookup to check for an incomplete expire. |
774 | * the flags, such as expire in progress, in case we're racing with expire. | ||
775 | * | 749 | * |
776 | * If a process is blocked on the dentry waiting for the expire to finish, | 750 | * If a process is blocked on the dentry waiting for the expire to finish, |
777 | * it will invalidate the dentry and try to mount with a new one. | 751 | * it will invalidate the dentry and try to mount with a new one. |
@@ -801,9 +775,9 @@ static int autofs4_dir_unlink(struct inode *dir, struct dentry *dentry) | |||
801 | dir->i_mtime = CURRENT_TIME; | 775 | dir->i_mtime = CURRENT_TIME; |
802 | 776 | ||
803 | spin_lock(&dcache_lock); | 777 | spin_lock(&dcache_lock); |
804 | spin_lock(&sbi->rehash_lock); | 778 | spin_lock(&sbi->lookup_lock); |
805 | list_add(&ino->rehash, &sbi->rehash_list); | 779 | list_add(&ino->expiring, &sbi->expiring_list); |
806 | spin_unlock(&sbi->rehash_lock); | 780 | spin_unlock(&sbi->lookup_lock); |
807 | spin_lock(&dentry->d_lock); | 781 | spin_lock(&dentry->d_lock); |
808 | __d_drop(dentry); | 782 | __d_drop(dentry); |
809 | spin_unlock(&dentry->d_lock); | 783 | spin_unlock(&dentry->d_lock); |
@@ -829,9 +803,9 @@ static int autofs4_dir_rmdir(struct inode *dir, struct dentry *dentry) | |||
829 | spin_unlock(&dcache_lock); | 803 | spin_unlock(&dcache_lock); |
830 | return -ENOTEMPTY; | 804 | return -ENOTEMPTY; |
831 | } | 805 | } |
832 | spin_lock(&sbi->rehash_lock); | 806 | spin_lock(&sbi->lookup_lock); |
833 | list_add(&ino->rehash, &sbi->rehash_list); | 807 | list_add(&ino->expiring, &sbi->expiring_list); |
834 | spin_unlock(&sbi->rehash_lock); | 808 | spin_unlock(&sbi->lookup_lock); |
835 | spin_lock(&dentry->d_lock); | 809 | spin_lock(&dentry->d_lock); |
836 | __d_drop(dentry); | 810 | __d_drop(dentry); |
837 | spin_unlock(&dentry->d_lock); | 811 | spin_unlock(&dentry->d_lock); |