Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

fs/namei.c: keep track of nd->root refcount status

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.

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>

Al Viro 84a2bd39 ee594bff

+16 -26
+15 -26
fs/namei.c
··· 596 596 path_put(&nd->path); 597 597 for (i = 0; i < nd->depth; i++) 598 598 path_put(&nd->stack[i].link); 599 - if (nd->root.mnt && !(nd->flags & LOOKUP_ROOT)) { 599 + if (nd->flags & LOOKUP_ROOT_GRABBED) { 600 600 path_put(&nd->root); 601 - nd->root.mnt = NULL; 601 + nd->flags &= ~LOOKUP_ROOT_GRABBED; 602 602 } 603 603 } else { 604 604 nd->flags &= ~LOOKUP_RCU; 605 - if (!(nd->flags & LOOKUP_ROOT)) 606 - nd->root.mnt = NULL; 607 605 rcu_read_unlock(); 608 606 } 609 607 nd->depth = 0; ··· 643 645 { 644 646 if (!nd->root.mnt || (nd->flags & LOOKUP_ROOT)) 645 647 return true; 648 + nd->flags |= LOOKUP_ROOT_GRABBED; 646 649 return legitimize_path(nd, &nd->root, nd->root_seq); 647 650 } 648 651 ··· 677 678 678 679 nd->flags &= ~LOOKUP_RCU; 679 680 if (unlikely(!legitimize_links(nd))) 680 - goto out2; 681 - if (unlikely(!legitimize_path(nd, &nd->path, nd->seq))) 682 681 goto out1; 682 + if (unlikely(!legitimize_path(nd, &nd->path, nd->seq))) 683 + goto out; 683 684 if (unlikely(!legitimize_root(nd))) 684 685 goto out; 685 686 rcu_read_unlock(); 686 687 BUG_ON(nd->inode != parent->d_inode); 687 688 return 0; 688 689 689 - out2: 690 + out1: 690 691 nd->path.mnt = NULL; 691 692 nd->path.dentry = NULL; 692 - out1: 693 - if (!(nd->flags & LOOKUP_ROOT)) 694 - nd->root.mnt = NULL; 695 693 out: 696 694 rcu_read_unlock(); 697 695 return -ECHILD; ··· 728 732 */ 729 733 if (unlikely(!lockref_get_not_dead(&dentry->d_lockref))) 730 734 goto out; 731 - if (unlikely(read_seqcount_retry(&dentry->d_seq, seq))) { 732 - rcu_read_unlock(); 733 - dput(dentry); 734 - goto drop_root_mnt; 735 - } 735 + if (unlikely(read_seqcount_retry(&dentry->d_seq, seq))) 736 + goto out_dput; 736 737 /* 737 738 * Sequence counts matched. Now make sure that the root is 738 739 * still valid and get it if required. 739 740 */ 740 - if (unlikely(!legitimize_root(nd))) { 741 - rcu_read_unlock(); 742 - dput(dentry); 743 - return -ECHILD; 744 - } 745 - 741 + if (unlikely(!legitimize_root(nd))) 742 + goto out_dput; 746 743 rcu_read_unlock(); 747 744 return 0; 748 745 ··· 745 756 nd->path.dentry = NULL; 746 757 out: 747 758 rcu_read_unlock(); 748 - drop_root_mnt: 749 - if (!(nd->flags & LOOKUP_ROOT)) 750 - nd->root.mnt = NULL; 759 + return -ECHILD; 760 + out_dput: 761 + rcu_read_unlock(); 762 + dput(dentry); 751 763 return -ECHILD; 752 764 } 753 765 ··· 812 822 } while (read_seqcount_retry(&fs->seq, seq)); 813 823 } else { 814 824 get_fs_root(fs, &nd->root); 825 + nd->flags |= LOOKUP_ROOT_GRABBED; 815 826 } 816 827 } 817 828 ··· 1729 1738 nd->flags &= ~LOOKUP_RCU; 1730 1739 nd->path.mnt = NULL; 1731 1740 nd->path.dentry = NULL; 1732 - if (!(nd->flags & LOOKUP_ROOT)) 1733 - nd->root.mnt = NULL; 1734 1741 rcu_read_unlock(); 1735 1742 } else if (likely(unlazy_walk(nd)) == 0) 1736 1743 error = nd_alloc_stack(nd);
+1
include/linux/namei.h
··· 37 37 #define LOOKUP_NO_REVAL 0x0080 38 38 #define LOOKUP_JUMPED 0x1000 39 39 #define LOOKUP_ROOT 0x2000 40 + #define LOOKUP_ROOT_GRABBED 0x0008 40 41 41 42 extern int path_pts(struct path *path); 42 43