diff options
Diffstat (limited to 'fs/namespace.c')
-rw-r--r-- | fs/namespace.c | 223 |
1 files changed, 128 insertions, 95 deletions
diff --git a/fs/namespace.c b/fs/namespace.c index 3ddfd9046c44..7b0b95371696 100644 --- a/fs/namespace.c +++ b/fs/namespace.c | |||
@@ -183,7 +183,7 @@ static inline void mnt_dec_count(struct vfsmount *mnt) | |||
183 | unsigned int mnt_get_count(struct vfsmount *mnt) | 183 | unsigned int mnt_get_count(struct vfsmount *mnt) |
184 | { | 184 | { |
185 | #ifdef CONFIG_SMP | 185 | #ifdef CONFIG_SMP |
186 | unsigned int count = atomic_read(&mnt->mnt_longrefs); | 186 | unsigned int count = 0; |
187 | int cpu; | 187 | int cpu; |
188 | 188 | ||
189 | for_each_possible_cpu(cpu) { | 189 | for_each_possible_cpu(cpu) { |
@@ -217,7 +217,7 @@ struct vfsmount *alloc_vfsmnt(const char *name) | |||
217 | if (!mnt->mnt_pcp) | 217 | if (!mnt->mnt_pcp) |
218 | goto out_free_devname; | 218 | goto out_free_devname; |
219 | 219 | ||
220 | atomic_set(&mnt->mnt_longrefs, 1); | 220 | this_cpu_add(mnt->mnt_pcp->mnt_count, 1); |
221 | #else | 221 | #else |
222 | mnt->mnt_count = 1; | 222 | mnt->mnt_count = 1; |
223 | mnt->mnt_writers = 0; | 223 | mnt->mnt_writers = 0; |
@@ -611,6 +611,21 @@ static void attach_mnt(struct vfsmount *mnt, struct path *path) | |||
611 | list_add_tail(&mnt->mnt_child, &path->mnt->mnt_mounts); | 611 | list_add_tail(&mnt->mnt_child, &path->mnt->mnt_mounts); |
612 | } | 612 | } |
613 | 613 | ||
614 | static inline void __mnt_make_longterm(struct vfsmount *mnt) | ||
615 | { | ||
616 | #ifdef CONFIG_SMP | ||
617 | atomic_inc(&mnt->mnt_longterm); | ||
618 | #endif | ||
619 | } | ||
620 | |||
621 | /* needs vfsmount lock for write */ | ||
622 | static inline void __mnt_make_shortterm(struct vfsmount *mnt) | ||
623 | { | ||
624 | #ifdef CONFIG_SMP | ||
625 | atomic_dec(&mnt->mnt_longterm); | ||
626 | #endif | ||
627 | } | ||
628 | |||
614 | /* | 629 | /* |
615 | * vfsmount lock must be held for write | 630 | * vfsmount lock must be held for write |
616 | */ | 631 | */ |
@@ -624,8 +639,11 @@ static void commit_tree(struct vfsmount *mnt) | |||
624 | BUG_ON(parent == mnt); | 639 | BUG_ON(parent == mnt); |
625 | 640 | ||
626 | list_add_tail(&head, &mnt->mnt_list); | 641 | list_add_tail(&head, &mnt->mnt_list); |
627 | list_for_each_entry(m, &head, mnt_list) | 642 | list_for_each_entry(m, &head, mnt_list) { |
628 | m->mnt_ns = n; | 643 | m->mnt_ns = n; |
644 | __mnt_make_longterm(m); | ||
645 | } | ||
646 | |||
629 | list_splice(&head, n->list.prev); | 647 | list_splice(&head, n->list.prev); |
630 | 648 | ||
631 | list_add_tail(&mnt->mnt_hash, mount_hashtable + | 649 | list_add_tail(&mnt->mnt_hash, mount_hashtable + |
@@ -734,51 +752,30 @@ static inline void mntfree(struct vfsmount *mnt) | |||
734 | deactivate_super(sb); | 752 | deactivate_super(sb); |
735 | } | 753 | } |
736 | 754 | ||
737 | #ifdef CONFIG_SMP | 755 | static void mntput_no_expire(struct vfsmount *mnt) |
738 | static inline void __mntput(struct vfsmount *mnt, int longrefs) | ||
739 | { | 756 | { |
740 | if (!longrefs) { | ||
741 | put_again: | 757 | put_again: |
742 | br_read_lock(vfsmount_lock); | 758 | #ifdef CONFIG_SMP |
743 | if (likely(atomic_read(&mnt->mnt_longrefs))) { | 759 | br_read_lock(vfsmount_lock); |
744 | mnt_dec_count(mnt); | 760 | if (likely(atomic_read(&mnt->mnt_longterm))) { |
745 | br_read_unlock(vfsmount_lock); | 761 | mnt_dec_count(mnt); |
746 | return; | ||
747 | } | ||
748 | br_read_unlock(vfsmount_lock); | 762 | br_read_unlock(vfsmount_lock); |
749 | } else { | 763 | return; |
750 | BUG_ON(!atomic_read(&mnt->mnt_longrefs)); | ||
751 | if (atomic_add_unless(&mnt->mnt_longrefs, -1, 1)) | ||
752 | return; | ||
753 | } | 764 | } |
765 | br_read_unlock(vfsmount_lock); | ||
754 | 766 | ||
755 | br_write_lock(vfsmount_lock); | 767 | br_write_lock(vfsmount_lock); |
756 | if (!longrefs) | 768 | mnt_dec_count(mnt); |
757 | mnt_dec_count(mnt); | ||
758 | else | ||
759 | atomic_dec(&mnt->mnt_longrefs); | ||
760 | if (mnt_get_count(mnt)) { | 769 | if (mnt_get_count(mnt)) { |
761 | br_write_unlock(vfsmount_lock); | 770 | br_write_unlock(vfsmount_lock); |
762 | return; | 771 | return; |
763 | } | 772 | } |
764 | if (unlikely(mnt->mnt_pinned)) { | ||
765 | mnt_add_count(mnt, mnt->mnt_pinned + 1); | ||
766 | mnt->mnt_pinned = 0; | ||
767 | br_write_unlock(vfsmount_lock); | ||
768 | acct_auto_close_mnt(mnt); | ||
769 | goto put_again; | ||
770 | } | ||
771 | br_write_unlock(vfsmount_lock); | ||
772 | mntfree(mnt); | ||
773 | } | ||
774 | #else | 773 | #else |
775 | static inline void __mntput(struct vfsmount *mnt, int longrefs) | ||
776 | { | ||
777 | put_again: | ||
778 | mnt_dec_count(mnt); | 774 | mnt_dec_count(mnt); |
779 | if (likely(mnt_get_count(mnt))) | 775 | if (likely(mnt_get_count(mnt))) |
780 | return; | 776 | return; |
781 | br_write_lock(vfsmount_lock); | 777 | br_write_lock(vfsmount_lock); |
778 | #endif | ||
782 | if (unlikely(mnt->mnt_pinned)) { | 779 | if (unlikely(mnt->mnt_pinned)) { |
783 | mnt_add_count(mnt, mnt->mnt_pinned + 1); | 780 | mnt_add_count(mnt, mnt->mnt_pinned + 1); |
784 | mnt->mnt_pinned = 0; | 781 | mnt->mnt_pinned = 0; |
@@ -789,12 +786,6 @@ put_again: | |||
789 | br_write_unlock(vfsmount_lock); | 786 | br_write_unlock(vfsmount_lock); |
790 | mntfree(mnt); | 787 | mntfree(mnt); |
791 | } | 788 | } |
792 | #endif | ||
793 | |||
794 | static void mntput_no_expire(struct vfsmount *mnt) | ||
795 | { | ||
796 | __mntput(mnt, 0); | ||
797 | } | ||
798 | 789 | ||
799 | void mntput(struct vfsmount *mnt) | 790 | void mntput(struct vfsmount *mnt) |
800 | { | 791 | { |
@@ -802,7 +793,7 @@ void mntput(struct vfsmount *mnt) | |||
802 | /* avoid cacheline pingpong, hope gcc doesn't get "smart" */ | 793 | /* avoid cacheline pingpong, hope gcc doesn't get "smart" */ |
803 | if (unlikely(mnt->mnt_expiry_mark)) | 794 | if (unlikely(mnt->mnt_expiry_mark)) |
804 | mnt->mnt_expiry_mark = 0; | 795 | mnt->mnt_expiry_mark = 0; |
805 | __mntput(mnt, 0); | 796 | mntput_no_expire(mnt); |
806 | } | 797 | } |
807 | } | 798 | } |
808 | EXPORT_SYMBOL(mntput); | 799 | EXPORT_SYMBOL(mntput); |
@@ -815,33 +806,6 @@ struct vfsmount *mntget(struct vfsmount *mnt) | |||
815 | } | 806 | } |
816 | EXPORT_SYMBOL(mntget); | 807 | EXPORT_SYMBOL(mntget); |
817 | 808 | ||
818 | void mntput_long(struct vfsmount *mnt) | ||
819 | { | ||
820 | #ifdef CONFIG_SMP | ||
821 | if (mnt) { | ||
822 | /* avoid cacheline pingpong, hope gcc doesn't get "smart" */ | ||
823 | if (unlikely(mnt->mnt_expiry_mark)) | ||
824 | mnt->mnt_expiry_mark = 0; | ||
825 | __mntput(mnt, 1); | ||
826 | } | ||
827 | #else | ||
828 | mntput(mnt); | ||
829 | #endif | ||
830 | } | ||
831 | EXPORT_SYMBOL(mntput_long); | ||
832 | |||
833 | struct vfsmount *mntget_long(struct vfsmount *mnt) | ||
834 | { | ||
835 | #ifdef CONFIG_SMP | ||
836 | if (mnt) | ||
837 | atomic_inc(&mnt->mnt_longrefs); | ||
838 | return mnt; | ||
839 | #else | ||
840 | return mntget(mnt); | ||
841 | #endif | ||
842 | } | ||
843 | EXPORT_SYMBOL(mntget_long); | ||
844 | |||
845 | void mnt_pin(struct vfsmount *mnt) | 809 | void mnt_pin(struct vfsmount *mnt) |
846 | { | 810 | { |
847 | br_write_lock(vfsmount_lock); | 811 | br_write_lock(vfsmount_lock); |
@@ -1216,7 +1180,7 @@ void release_mounts(struct list_head *head) | |||
1216 | dput(dentry); | 1180 | dput(dentry); |
1217 | mntput(m); | 1181 | mntput(m); |
1218 | } | 1182 | } |
1219 | mntput_long(mnt); | 1183 | mntput(mnt); |
1220 | } | 1184 | } |
1221 | } | 1185 | } |
1222 | 1186 | ||
@@ -1226,19 +1190,21 @@ void release_mounts(struct list_head *head) | |||
1226 | */ | 1190 | */ |
1227 | void umount_tree(struct vfsmount *mnt, int propagate, struct list_head *kill) | 1191 | void umount_tree(struct vfsmount *mnt, int propagate, struct list_head *kill) |
1228 | { | 1192 | { |
1193 | LIST_HEAD(tmp_list); | ||
1229 | struct vfsmount *p; | 1194 | struct vfsmount *p; |
1230 | 1195 | ||
1231 | for (p = mnt; p; p = next_mnt(p, mnt)) | 1196 | for (p = mnt; p; p = next_mnt(p, mnt)) |
1232 | list_move(&p->mnt_hash, kill); | 1197 | list_move(&p->mnt_hash, &tmp_list); |
1233 | 1198 | ||
1234 | if (propagate) | 1199 | if (propagate) |
1235 | propagate_umount(kill); | 1200 | propagate_umount(&tmp_list); |
1236 | 1201 | ||
1237 | list_for_each_entry(p, kill, mnt_hash) { | 1202 | list_for_each_entry(p, &tmp_list, mnt_hash) { |
1238 | list_del_init(&p->mnt_expire); | 1203 | list_del_init(&p->mnt_expire); |
1239 | list_del_init(&p->mnt_list); | 1204 | list_del_init(&p->mnt_list); |
1240 | __touch_mnt_namespace(p->mnt_ns); | 1205 | __touch_mnt_namespace(p->mnt_ns); |
1241 | p->mnt_ns = NULL; | 1206 | p->mnt_ns = NULL; |
1207 | __mnt_make_shortterm(p); | ||
1242 | list_del_init(&p->mnt_child); | 1208 | list_del_init(&p->mnt_child); |
1243 | if (p->mnt_parent != p) { | 1209 | if (p->mnt_parent != p) { |
1244 | p->mnt_parent->mnt_ghosts++; | 1210 | p->mnt_parent->mnt_ghosts++; |
@@ -1246,6 +1212,7 @@ void umount_tree(struct vfsmount *mnt, int propagate, struct list_head *kill) | |||
1246 | } | 1212 | } |
1247 | change_mnt_propagation(p, MS_PRIVATE); | 1213 | change_mnt_propagation(p, MS_PRIVATE); |
1248 | } | 1214 | } |
1215 | list_splice(&tmp_list, kill); | ||
1249 | } | 1216 | } |
1250 | 1217 | ||
1251 | static void shrink_submounts(struct vfsmount *mnt, struct list_head *umounts); | 1218 | static void shrink_submounts(struct vfsmount *mnt, struct list_head *umounts); |
@@ -1844,9 +1811,10 @@ static int do_move_mount(struct path *path, char *old_name) | |||
1844 | return err; | 1811 | return err; |
1845 | 1812 | ||
1846 | down_write(&namespace_sem); | 1813 | down_write(&namespace_sem); |
1847 | while (d_mountpoint(path->dentry) && | 1814 | err = follow_down(path, true); |
1848 | follow_down(path)) | 1815 | if (err < 0) |
1849 | ; | 1816 | goto out; |
1817 | |||
1850 | err = -EINVAL; | 1818 | err = -EINVAL; |
1851 | if (!check_mnt(path->mnt) || !check_mnt(old_path.mnt)) | 1819 | if (!check_mnt(path->mnt) || !check_mnt(old_path.mnt)) |
1852 | goto out; | 1820 | goto out; |
@@ -1904,6 +1872,8 @@ out: | |||
1904 | return err; | 1872 | return err; |
1905 | } | 1873 | } |
1906 | 1874 | ||
1875 | static int do_add_mount(struct vfsmount *, struct path *, int); | ||
1876 | |||
1907 | /* | 1877 | /* |
1908 | * create a new mount for userspace and request it to be added into the | 1878 | * create a new mount for userspace and request it to be added into the |
1909 | * namespace's tree | 1879 | * namespace's tree |
@@ -1912,6 +1882,7 @@ static int do_new_mount(struct path *path, char *type, int flags, | |||
1912 | int mnt_flags, char *name, void *data) | 1882 | int mnt_flags, char *name, void *data) |
1913 | { | 1883 | { |
1914 | struct vfsmount *mnt; | 1884 | struct vfsmount *mnt; |
1885 | int err; | ||
1915 | 1886 | ||
1916 | if (!type) | 1887 | if (!type) |
1917 | return -EINVAL; | 1888 | return -EINVAL; |
@@ -1924,15 +1895,47 @@ static int do_new_mount(struct path *path, char *type, int flags, | |||
1924 | if (IS_ERR(mnt)) | 1895 | if (IS_ERR(mnt)) |
1925 | return PTR_ERR(mnt); | 1896 | return PTR_ERR(mnt); |
1926 | 1897 | ||
1927 | return do_add_mount(mnt, path, mnt_flags, NULL); | 1898 | err = do_add_mount(mnt, path, mnt_flags); |
1899 | if (err) | ||
1900 | mntput(mnt); | ||
1901 | return err; | ||
1902 | } | ||
1903 | |||
1904 | int finish_automount(struct vfsmount *m, struct path *path) | ||
1905 | { | ||
1906 | int err; | ||
1907 | /* The new mount record should have at least 2 refs to prevent it being | ||
1908 | * expired before we get a chance to add it | ||
1909 | */ | ||
1910 | BUG_ON(mnt_get_count(m) < 2); | ||
1911 | |||
1912 | if (m->mnt_sb == path->mnt->mnt_sb && | ||
1913 | m->mnt_root == path->dentry) { | ||
1914 | err = -ELOOP; | ||
1915 | goto fail; | ||
1916 | } | ||
1917 | |||
1918 | err = do_add_mount(m, path, path->mnt->mnt_flags | MNT_SHRINKABLE); | ||
1919 | if (!err) | ||
1920 | return 0; | ||
1921 | fail: | ||
1922 | /* remove m from any expiration list it may be on */ | ||
1923 | if (!list_empty(&m->mnt_expire)) { | ||
1924 | down_write(&namespace_sem); | ||
1925 | br_write_lock(vfsmount_lock); | ||
1926 | list_del_init(&m->mnt_expire); | ||
1927 | br_write_unlock(vfsmount_lock); | ||
1928 | up_write(&namespace_sem); | ||
1929 | } | ||
1930 | mntput(m); | ||
1931 | mntput(m); | ||
1932 | return err; | ||
1928 | } | 1933 | } |
1929 | 1934 | ||
1930 | /* | 1935 | /* |
1931 | * add a mount into a namespace's mount tree | 1936 | * add a mount into a namespace's mount tree |
1932 | * - provide the option of adding the new mount to an expiration list | ||
1933 | */ | 1937 | */ |
1934 | int do_add_mount(struct vfsmount *newmnt, struct path *path, | 1938 | static int do_add_mount(struct vfsmount *newmnt, struct path *path, int mnt_flags) |
1935 | int mnt_flags, struct list_head *fslist) | ||
1936 | { | 1939 | { |
1937 | int err; | 1940 | int err; |
1938 | 1941 | ||
@@ -1940,9 +1943,10 @@ int do_add_mount(struct vfsmount *newmnt, struct path *path, | |||
1940 | 1943 | ||
1941 | down_write(&namespace_sem); | 1944 | down_write(&namespace_sem); |
1942 | /* Something was mounted here while we slept */ | 1945 | /* Something was mounted here while we slept */ |
1943 | while (d_mountpoint(path->dentry) && | 1946 | err = follow_down(path, true); |
1944 | follow_down(path)) | 1947 | if (err < 0) |
1945 | ; | 1948 | goto unlock; |
1949 | |||
1946 | err = -EINVAL; | 1950 | err = -EINVAL; |
1947 | if (!(mnt_flags & MNT_SHRINKABLE) && !check_mnt(path->mnt)) | 1951 | if (!(mnt_flags & MNT_SHRINKABLE) && !check_mnt(path->mnt)) |
1948 | goto unlock; | 1952 | goto unlock; |
@@ -1958,22 +1962,29 @@ int do_add_mount(struct vfsmount *newmnt, struct path *path, | |||
1958 | goto unlock; | 1962 | goto unlock; |
1959 | 1963 | ||
1960 | newmnt->mnt_flags = mnt_flags; | 1964 | newmnt->mnt_flags = mnt_flags; |
1961 | if ((err = graft_tree(newmnt, path))) | 1965 | err = graft_tree(newmnt, path); |
1962 | goto unlock; | ||
1963 | |||
1964 | if (fslist) /* add to the specified expiration list */ | ||
1965 | list_add_tail(&newmnt->mnt_expire, fslist); | ||
1966 | |||
1967 | up_write(&namespace_sem); | ||
1968 | return 0; | ||
1969 | 1966 | ||
1970 | unlock: | 1967 | unlock: |
1971 | up_write(&namespace_sem); | 1968 | up_write(&namespace_sem); |
1972 | mntput_long(newmnt); | ||
1973 | return err; | 1969 | return err; |
1974 | } | 1970 | } |
1975 | 1971 | ||
1976 | EXPORT_SYMBOL_GPL(do_add_mount); | 1972 | /** |
1973 | * mnt_set_expiry - Put a mount on an expiration list | ||
1974 | * @mnt: The mount to list. | ||
1975 | * @expiry_list: The list to add the mount to. | ||
1976 | */ | ||
1977 | void mnt_set_expiry(struct vfsmount *mnt, struct list_head *expiry_list) | ||
1978 | { | ||
1979 | down_write(&namespace_sem); | ||
1980 | br_write_lock(vfsmount_lock); | ||
1981 | |||
1982 | list_add_tail(&mnt->mnt_expire, expiry_list); | ||
1983 | |||
1984 | br_write_unlock(vfsmount_lock); | ||
1985 | up_write(&namespace_sem); | ||
1986 | } | ||
1987 | EXPORT_SYMBOL(mnt_set_expiry); | ||
1977 | 1988 | ||
1978 | /* | 1989 | /* |
1979 | * process a list of expirable mountpoints with the intent of discarding any | 1990 | * process a list of expirable mountpoints with the intent of discarding any |
@@ -2262,6 +2273,22 @@ static struct mnt_namespace *alloc_mnt_ns(void) | |||
2262 | return new_ns; | 2273 | return new_ns; |
2263 | } | 2274 | } |
2264 | 2275 | ||
2276 | void mnt_make_longterm(struct vfsmount *mnt) | ||
2277 | { | ||
2278 | __mnt_make_longterm(mnt); | ||
2279 | } | ||
2280 | |||
2281 | void mnt_make_shortterm(struct vfsmount *mnt) | ||
2282 | { | ||
2283 | #ifdef CONFIG_SMP | ||
2284 | if (atomic_add_unless(&mnt->mnt_longterm, -1, 1)) | ||
2285 | return; | ||
2286 | br_write_lock(vfsmount_lock); | ||
2287 | atomic_dec(&mnt->mnt_longterm); | ||
2288 | br_write_unlock(vfsmount_lock); | ||
2289 | #endif | ||
2290 | } | ||
2291 | |||
2265 | /* | 2292 | /* |
2266 | * Allocate a new namespace structure and populate it with contents | 2293 | * Allocate a new namespace structure and populate it with contents |
2267 | * copied from the namespace of the passed in task structure. | 2294 | * copied from the namespace of the passed in task structure. |
@@ -2299,14 +2326,19 @@ static struct mnt_namespace *dup_mnt_ns(struct mnt_namespace *mnt_ns, | |||
2299 | q = new_ns->root; | 2326 | q = new_ns->root; |
2300 | while (p) { | 2327 | while (p) { |
2301 | q->mnt_ns = new_ns; | 2328 | q->mnt_ns = new_ns; |
2329 | __mnt_make_longterm(q); | ||
2302 | if (fs) { | 2330 | if (fs) { |
2303 | if (p == fs->root.mnt) { | 2331 | if (p == fs->root.mnt) { |
2332 | fs->root.mnt = mntget(q); | ||
2333 | __mnt_make_longterm(q); | ||
2334 | mnt_make_shortterm(p); | ||
2304 | rootmnt = p; | 2335 | rootmnt = p; |
2305 | fs->root.mnt = mntget_long(q); | ||
2306 | } | 2336 | } |
2307 | if (p == fs->pwd.mnt) { | 2337 | if (p == fs->pwd.mnt) { |
2338 | fs->pwd.mnt = mntget(q); | ||
2339 | __mnt_make_longterm(q); | ||
2340 | mnt_make_shortterm(p); | ||
2308 | pwdmnt = p; | 2341 | pwdmnt = p; |
2309 | fs->pwd.mnt = mntget_long(q); | ||
2310 | } | 2342 | } |
2311 | } | 2343 | } |
2312 | p = next_mnt(p, mnt_ns->root); | 2344 | p = next_mnt(p, mnt_ns->root); |
@@ -2315,9 +2347,9 @@ static struct mnt_namespace *dup_mnt_ns(struct mnt_namespace *mnt_ns, | |||
2315 | up_write(&namespace_sem); | 2347 | up_write(&namespace_sem); |
2316 | 2348 | ||
2317 | if (rootmnt) | 2349 | if (rootmnt) |
2318 | mntput_long(rootmnt); | 2350 | mntput(rootmnt); |
2319 | if (pwdmnt) | 2351 | if (pwdmnt) |
2320 | mntput_long(pwdmnt); | 2352 | mntput(pwdmnt); |
2321 | 2353 | ||
2322 | return new_ns; | 2354 | return new_ns; |
2323 | } | 2355 | } |
@@ -2350,6 +2382,7 @@ struct mnt_namespace *create_mnt_ns(struct vfsmount *mnt) | |||
2350 | new_ns = alloc_mnt_ns(); | 2382 | new_ns = alloc_mnt_ns(); |
2351 | if (!IS_ERR(new_ns)) { | 2383 | if (!IS_ERR(new_ns)) { |
2352 | mnt->mnt_ns = new_ns; | 2384 | mnt->mnt_ns = new_ns; |
2385 | __mnt_make_longterm(mnt); | ||
2353 | new_ns->root = mnt; | 2386 | new_ns->root = mnt; |
2354 | list_add(&new_ns->list, &new_ns->root->mnt_list); | 2387 | list_add(&new_ns->list, &new_ns->root->mnt_list); |
2355 | } | 2388 | } |