The rules for nd->root are messy: * if we have LOOKUP_ROOT, it doesn't contribute to refcounts * if we have LOOKUP_RCU, it doesn't contribute to refcounts * if nd->root.mnt is NULL, it doesn't contribute to refcounts * otherwise it does contribute terminate_walk() needs to drop the references if they are contributing. So everything else should be careful not to confuse it, leading to rather convoluted code. It's easier to keep track of whether we'd grabbed the reference(s) explicitly. Use a new flag for that. Don't bother with zeroing nd->root.mnt on unlazy failures and in terminate_walk - it's not needed anymore (terminate_walk() won't care and the next path_init() will zero nd->root in !LOOKUP_ROOT case anyway). Resulting rules for nd->root refcounts are much simpler: they are contributing iff LOOKUP_ROOT_GRABBED is set in nd->flags.
Unified · Split
| @@ -596,14 +596,12 @@ static void terminate_walk(struct nameidata *nd) · 595 unmodified lines | |||
| 596 | path_put(&nd->path); | 596 | path_put(&nd->path); |
| 597 | for (i = 0; i < nd->depth; i++) | 597 | for (i = 0; i < nd->depth; i++) |
| 598 | path_put(&nd->stack[i].link); | 598 | path_put(&nd->stack[i].link); |
| 599 | if (nd->root.mnt && !(nd->flags & LOOKUP_ROOT)) { | 599 | if (nd->flags & LOOKUP_ROOT_GRABBED) { |
| 600 | path_put(&nd->root); | 600 | path_put(&nd->root); |
| 601 | nd->root.mnt = NULL; | 601 | nd->flags &= ~LOOKUP_ROOT_GRABBED; |
| 602 | } | 602 | } |
| 603 | } else { | 603 | } else { |
| 604 | nd->flags &= ~LOOKUP_RCU; | 604 | nd->flags &= ~LOOKUP_RCU; |
| 605 | if (!(nd->flags & LOOKUP_ROOT)) | ||
| 606 | nd->root.mnt = NULL; | ||
| 607 | rcu_read_unlock(); | 605 | rcu_read_unlock(); |
| 608 | } | 606 | } |
| 609 | nd->depth = 0; | 607 | nd->depth = 0; |
| @@ -645,6 +643,7 @@ static bool legitimize_root(struct nameidata *nd) · 35 unmodified lines | |||
| 645 | { | 643 | { |
| 646 | if (!nd->root.mnt || (nd->flags & LOOKUP_ROOT)) | 644 | if (!nd->root.mnt || (nd->flags & LOOKUP_ROOT)) |
| 647 | return true; | 645 | return true; |
| 646 | nd->flags |= LOOKUP_ROOT_GRABBED; | ||
| 648 | return legitimize_path(nd, &nd->root, nd->root_seq); | 647 | return legitimize_path(nd, &nd->root, nd->root_seq); |
| 649 | } | 648 | } |
| 650 | 649 | ||
| @@ -678,21 +677,18 @@ static int unlazy_walk(struct nameidata *nd) · 27 unmodified lines | |||
| 678 | 677 | ||
| 679 | nd->flags &= ~LOOKUP_RCU; | 678 | nd->flags &= ~LOOKUP_RCU; |
| 680 | if (unlikely(!legitimize_links(nd))) | 679 | if (unlikely(!legitimize_links(nd))) |
| 681 | goto out2; | ||
| 682 | if (unlikely(!legitimize_path(nd, &nd->path, nd->seq))) | ||
| 683 | goto out1; | 680 | goto out1; |
| 681 | if (unlikely(!legitimize_path(nd, &nd->path, nd->seq))) | ||
| 682 | goto out; | ||
| 684 | if (unlikely(!legitimize_root(nd))) | 683 | if (unlikely(!legitimize_root(nd))) |
| 685 | goto out; | 684 | goto out; |
| 686 | rcu_read_unlock(); | 685 | rcu_read_unlock(); |
| 687 | BUG_ON(nd->inode != parent->d_inode); | 686 | BUG_ON(nd->inode != parent->d_inode); |
| 688 | return 0; | 687 | return 0; |
| 689 | 688 | ||
| 690 | out2: | 689 | out1: |
| 691 | nd->path.mnt = NULL; | 690 | nd->path.mnt = NULL; |
| 692 | nd->path.dentry = NULL; | 691 | nd->path.dentry = NULL; |
| 693 | out1: | ||
| 694 | if (!(nd->flags & LOOKUP_ROOT)) | ||
| 695 | nd->root.mnt = NULL; | ||
| 696 | out: | 692 | out: |
| 697 | rcu_read_unlock(); | 693 | rcu_read_unlock(); |
| 698 | return -ECHILD; | 694 | return -ECHILD; |
| @@ -732,21 +728,14 @@ static int unlazy_child(struct nameidata *nd, struct dentry *dentry, unsigned se · 33 unmodified lines | |||
| 732 | */ | 728 | */ |
| 733 | if (unlikely(!lockref_get_not_dead(&dentry->d_lockref))) | 729 | if (unlikely(!lockref_get_not_dead(&dentry->d_lockref))) |
| 734 | goto out; | 730 | goto out; |
| 735 | if (unlikely(read_seqcount_retry(&dentry->d_seq, seq))) { | 731 | if (unlikely(read_seqcount_retry(&dentry->d_seq, seq))) |
| 736 | rcu_read_unlock(); | 732 | goto out_dput; |
| 737 | dput(dentry); | ||
| 738 | goto drop_root_mnt; | ||
| 739 | } | ||
| 740 | /* | 733 | /* |
| 741 | * Sequence counts matched. Now make sure that the root is | 734 | * Sequence counts matched. Now make sure that the root is |
| 742 | * still valid and get it if required. | 735 | * still valid and get it if required. |
| 743 | */ | 736 | */ |
| 744 | if (unlikely(!legitimize_root(nd))) { | 737 | if (unlikely(!legitimize_root(nd))) |
| 745 | rcu_read_unlock(); | 738 | goto out_dput; |
| 746 | dput(dentry); | ||
| 747 | return -ECHILD; | ||
| 748 | } | ||
| 749 | |||
| 750 | rcu_read_unlock(); | 739 | rcu_read_unlock(); |
| 751 | return 0; | 740 | return 0; |
| 752 | 741 | ||
| @@ -756,9 +745,10 @@ out1: · 3 unmodified lines | |||
| 756 | nd->path.dentry = NULL; | 745 | nd->path.dentry = NULL; |
| 757 | out: | 746 | out: |
| 758 | rcu_read_unlock(); | 747 | rcu_read_unlock(); |
| 759 | drop_root_mnt: | 748 | return -ECHILD; |
| 760 | if (!(nd->flags & LOOKUP_ROOT)) | 749 | out_dput: |
| 761 | nd->root.mnt = NULL; | 750 | rcu_read_unlock(); |
| 751 | dput(dentry); | ||
| 762 | return -ECHILD; | 752 | return -ECHILD; |
| 763 | } | 753 | } |
| 764 | 754 | ||
| @@ -822,6 +812,7 @@ static void set_root(struct nameidata *nd) · 57 unmodified lines | |||
| 822 | } while (read_seqcount_retry(&fs->seq, seq)); | 812 | } while (read_seqcount_retry(&fs->seq, seq)); |
| 823 | } else { | 813 | } else { |
| 824 | get_fs_root(fs, &nd->root); | 814 | get_fs_root(fs, &nd->root); |
| 815 | nd->flags |= LOOKUP_ROOT_GRABBED; | ||
| 825 | } | 816 | } |
| 826 | } | 817 | } |
| 827 | 818 | ||
| @@ -1738,8 +1729,6 @@ static int pick_link(struct nameidata *nd, struct path *link, · 910 unmodified lines | |||
| 1738 | nd->flags &= ~LOOKUP_RCU; | 1729 | nd->flags &= ~LOOKUP_RCU; |
| 1739 | nd->path.mnt = NULL; | 1730 | nd->path.mnt = NULL; |
| 1740 | nd->path.dentry = NULL; | 1731 | nd->path.dentry = NULL; |
| 1741 | if (!(nd->flags & LOOKUP_ROOT)) | ||
| 1742 | nd->root.mnt = NULL; | ||
| 1743 | rcu_read_unlock(); | 1732 | rcu_read_unlock(); |
| 1744 | } else if (likely(unlazy_walk(nd)) == 0) | 1733 | } else if (likely(unlazy_walk(nd)) == 0) |
| 1745 | error = nd_alloc_stack(nd); | 1734 | error = nd_alloc_stack(nd); |
| @@ -37,6 +37,7 @@ enum {LAST_NORM, LAST_ROOT, LAST_DOT, LAST_DOTDOT, LAST_BIND}; · 36 unmodified lines | |||
| 37 | #define LOOKUP_NO_REVAL 0x0080 | 37 | #define LOOKUP_NO_REVAL 0x0080 |
| 38 | #define LOOKUP_JUMPED 0x1000 | 38 | #define LOOKUP_JUMPED 0x1000 |
| 39 | #define LOOKUP_ROOT 0x2000 | 39 | #define LOOKUP_ROOT 0x2000 |
| 40 | #define LOOKUP_ROOT_GRABBED 0x0008 | ||
| 40 | 41 | ||
| 41 | extern int path_pts(struct path *path); | 42 | extern int path_pts(struct path *path); |
| 42 | 43 | ||