diff options
-rw-r--r-- | fs/namespace.c | 93 | ||||
-rw-r--r-- | fs/pnode.c | 5 | ||||
-rw-r--r-- | include/linux/mount.h | 1 |
3 files changed, 95 insertions, 4 deletions
diff --git a/fs/namespace.c b/fs/namespace.c index 8ca6317cb401..cefa1d9939b0 100644 --- a/fs/namespace.c +++ b/fs/namespace.c | |||
@@ -41,6 +41,7 @@ __cacheline_aligned_in_smp DEFINE_SPINLOCK(vfsmount_lock); | |||
41 | 41 | ||
42 | static int event; | 42 | static int event; |
43 | static DEFINE_IDA(mnt_id_ida); | 43 | static DEFINE_IDA(mnt_id_ida); |
44 | static DEFINE_IDA(mnt_group_ida); | ||
44 | 45 | ||
45 | static struct list_head *mount_hashtable __read_mostly; | 46 | static struct list_head *mount_hashtable __read_mostly; |
46 | static struct kmem_cache *mnt_cache __read_mostly; | 47 | static struct kmem_cache *mnt_cache __read_mostly; |
@@ -83,6 +84,28 @@ static void mnt_free_id(struct vfsmount *mnt) | |||
83 | spin_unlock(&vfsmount_lock); | 84 | spin_unlock(&vfsmount_lock); |
84 | } | 85 | } |
85 | 86 | ||
87 | /* | ||
88 | * Allocate a new peer group ID | ||
89 | * | ||
90 | * mnt_group_ida is protected by namespace_sem | ||
91 | */ | ||
92 | static int mnt_alloc_group_id(struct vfsmount *mnt) | ||
93 | { | ||
94 | if (!ida_pre_get(&mnt_group_ida, GFP_KERNEL)) | ||
95 | return -ENOMEM; | ||
96 | |||
97 | return ida_get_new_above(&mnt_group_ida, 1, &mnt->mnt_group_id); | ||
98 | } | ||
99 | |||
100 | /* | ||
101 | * Release a peer group ID | ||
102 | */ | ||
103 | void mnt_release_group_id(struct vfsmount *mnt) | ||
104 | { | ||
105 | ida_remove(&mnt_group_ida, mnt->mnt_group_id); | ||
106 | mnt->mnt_group_id = 0; | ||
107 | } | ||
108 | |||
86 | struct vfsmount *alloc_vfsmnt(const char *name) | 109 | struct vfsmount *alloc_vfsmnt(const char *name) |
87 | { | 110 | { |
88 | struct vfsmount *mnt = kmem_cache_zalloc(mnt_cache, GFP_KERNEL); | 111 | struct vfsmount *mnt = kmem_cache_zalloc(mnt_cache, GFP_KERNEL); |
@@ -533,6 +556,17 @@ static struct vfsmount *clone_mnt(struct vfsmount *old, struct dentry *root, | |||
533 | struct vfsmount *mnt = alloc_vfsmnt(old->mnt_devname); | 556 | struct vfsmount *mnt = alloc_vfsmnt(old->mnt_devname); |
534 | 557 | ||
535 | if (mnt) { | 558 | if (mnt) { |
559 | if (flag & (CL_SLAVE | CL_PRIVATE)) | ||
560 | mnt->mnt_group_id = 0; /* not a peer of original */ | ||
561 | else | ||
562 | mnt->mnt_group_id = old->mnt_group_id; | ||
563 | |||
564 | if ((flag & CL_MAKE_SHARED) && !mnt->mnt_group_id) { | ||
565 | int err = mnt_alloc_group_id(mnt); | ||
566 | if (err) | ||
567 | goto out_free; | ||
568 | } | ||
569 | |||
536 | mnt->mnt_flags = old->mnt_flags; | 570 | mnt->mnt_flags = old->mnt_flags; |
537 | atomic_inc(&sb->s_active); | 571 | atomic_inc(&sb->s_active); |
538 | mnt->mnt_sb = sb; | 572 | mnt->mnt_sb = sb; |
@@ -562,6 +596,10 @@ static struct vfsmount *clone_mnt(struct vfsmount *old, struct dentry *root, | |||
562 | } | 596 | } |
563 | } | 597 | } |
564 | return mnt; | 598 | return mnt; |
599 | |||
600 | out_free: | ||
601 | free_vfsmnt(mnt); | ||
602 | return NULL; | ||
565 | } | 603 | } |
566 | 604 | ||
567 | static inline void __mntput(struct vfsmount *mnt) | 605 | static inline void __mntput(struct vfsmount *mnt) |
@@ -1142,6 +1180,33 @@ void drop_collected_mounts(struct vfsmount *mnt) | |||
1142 | release_mounts(&umount_list); | 1180 | release_mounts(&umount_list); |
1143 | } | 1181 | } |
1144 | 1182 | ||
1183 | static void cleanup_group_ids(struct vfsmount *mnt, struct vfsmount *end) | ||
1184 | { | ||
1185 | struct vfsmount *p; | ||
1186 | |||
1187 | for (p = mnt; p != end; p = next_mnt(p, mnt)) { | ||
1188 | if (p->mnt_group_id && !IS_MNT_SHARED(p)) | ||
1189 | mnt_release_group_id(p); | ||
1190 | } | ||
1191 | } | ||
1192 | |||
1193 | static int invent_group_ids(struct vfsmount *mnt, bool recurse) | ||
1194 | { | ||
1195 | struct vfsmount *p; | ||
1196 | |||
1197 | for (p = mnt; p; p = recurse ? next_mnt(p, mnt) : NULL) { | ||
1198 | if (!p->mnt_group_id && !IS_MNT_SHARED(p)) { | ||
1199 | int err = mnt_alloc_group_id(p); | ||
1200 | if (err) { | ||
1201 | cleanup_group_ids(mnt, p); | ||
1202 | return err; | ||
1203 | } | ||
1204 | } | ||
1205 | } | ||
1206 | |||
1207 | return 0; | ||
1208 | } | ||
1209 | |||
1145 | /* | 1210 | /* |
1146 | * @source_mnt : mount tree to be attached | 1211 | * @source_mnt : mount tree to be attached |
1147 | * @nd : place the mount tree @source_mnt is attached | 1212 | * @nd : place the mount tree @source_mnt is attached |
@@ -1212,9 +1277,16 @@ static int attach_recursive_mnt(struct vfsmount *source_mnt, | |||
1212 | struct vfsmount *dest_mnt = path->mnt; | 1277 | struct vfsmount *dest_mnt = path->mnt; |
1213 | struct dentry *dest_dentry = path->dentry; | 1278 | struct dentry *dest_dentry = path->dentry; |
1214 | struct vfsmount *child, *p; | 1279 | struct vfsmount *child, *p; |
1280 | int err; | ||
1215 | 1281 | ||
1216 | if (propagate_mnt(dest_mnt, dest_dentry, source_mnt, &tree_list)) | 1282 | if (IS_MNT_SHARED(dest_mnt)) { |
1217 | return -EINVAL; | 1283 | err = invent_group_ids(source_mnt, true); |
1284 | if (err) | ||
1285 | goto out; | ||
1286 | } | ||
1287 | err = propagate_mnt(dest_mnt, dest_dentry, source_mnt, &tree_list); | ||
1288 | if (err) | ||
1289 | goto out_cleanup_ids; | ||
1218 | 1290 | ||
1219 | if (IS_MNT_SHARED(dest_mnt)) { | 1291 | if (IS_MNT_SHARED(dest_mnt)) { |
1220 | for (p = source_mnt; p; p = next_mnt(p, source_mnt)) | 1292 | for (p = source_mnt; p; p = next_mnt(p, source_mnt)) |
@@ -1237,6 +1309,12 @@ static int attach_recursive_mnt(struct vfsmount *source_mnt, | |||
1237 | } | 1309 | } |
1238 | spin_unlock(&vfsmount_lock); | 1310 | spin_unlock(&vfsmount_lock); |
1239 | return 0; | 1311 | return 0; |
1312 | |||
1313 | out_cleanup_ids: | ||
1314 | if (IS_MNT_SHARED(dest_mnt)) | ||
1315 | cleanup_group_ids(source_mnt, NULL); | ||
1316 | out: | ||
1317 | return err; | ||
1240 | } | 1318 | } |
1241 | 1319 | ||
1242 | static int graft_tree(struct vfsmount *mnt, struct path *path) | 1320 | static int graft_tree(struct vfsmount *mnt, struct path *path) |
@@ -1277,6 +1355,7 @@ static noinline int do_change_type(struct nameidata *nd, int flag) | |||
1277 | struct vfsmount *m, *mnt = nd->path.mnt; | 1355 | struct vfsmount *m, *mnt = nd->path.mnt; |
1278 | int recurse = flag & MS_REC; | 1356 | int recurse = flag & MS_REC; |
1279 | int type = flag & ~MS_REC; | 1357 | int type = flag & ~MS_REC; |
1358 | int err = 0; | ||
1280 | 1359 | ||
1281 | if (!capable(CAP_SYS_ADMIN)) | 1360 | if (!capable(CAP_SYS_ADMIN)) |
1282 | return -EPERM; | 1361 | return -EPERM; |
@@ -1285,12 +1364,20 @@ static noinline int do_change_type(struct nameidata *nd, int flag) | |||
1285 | return -EINVAL; | 1364 | return -EINVAL; |
1286 | 1365 | ||
1287 | down_write(&namespace_sem); | 1366 | down_write(&namespace_sem); |
1367 | if (type == MS_SHARED) { | ||
1368 | err = invent_group_ids(mnt, recurse); | ||
1369 | if (err) | ||
1370 | goto out_unlock; | ||
1371 | } | ||
1372 | |||
1288 | spin_lock(&vfsmount_lock); | 1373 | spin_lock(&vfsmount_lock); |
1289 | for (m = mnt; m; m = (recurse ? next_mnt(m, mnt) : NULL)) | 1374 | for (m = mnt; m; m = (recurse ? next_mnt(m, mnt) : NULL)) |
1290 | change_mnt_propagation(m, type); | 1375 | change_mnt_propagation(m, type); |
1291 | spin_unlock(&vfsmount_lock); | 1376 | spin_unlock(&vfsmount_lock); |
1377 | |||
1378 | out_unlock: | ||
1292 | up_write(&namespace_sem); | 1379 | up_write(&namespace_sem); |
1293 | return 0; | 1380 | return err; |
1294 | } | 1381 | } |
1295 | 1382 | ||
1296 | /* | 1383 | /* |
diff --git a/fs/pnode.c b/fs/pnode.c index f968e35d9785..d18d66491a01 100644 --- a/fs/pnode.c +++ b/fs/pnode.c | |||
@@ -46,7 +46,11 @@ static int do_make_slave(struct vfsmount *mnt) | |||
46 | if (peer_mnt == mnt) | 46 | if (peer_mnt == mnt) |
47 | peer_mnt = NULL; | 47 | peer_mnt = NULL; |
48 | } | 48 | } |
49 | if (IS_MNT_SHARED(mnt) && list_empty(&mnt->mnt_share)) | ||
50 | mnt_release_group_id(mnt); | ||
51 | |||
49 | list_del_init(&mnt->mnt_share); | 52 | list_del_init(&mnt->mnt_share); |
53 | mnt->mnt_group_id = 0; | ||
50 | 54 | ||
51 | if (peer_mnt) | 55 | if (peer_mnt) |
52 | master = peer_mnt; | 56 | master = peer_mnt; |
@@ -68,7 +72,6 @@ static int do_make_slave(struct vfsmount *mnt) | |||
68 | } | 72 | } |
69 | mnt->mnt_master = master; | 73 | mnt->mnt_master = master; |
70 | CLEAR_MNT_SHARED(mnt); | 74 | CLEAR_MNT_SHARED(mnt); |
71 | INIT_LIST_HEAD(&mnt->mnt_slave_list); | ||
72 | return 0; | 75 | return 0; |
73 | } | 76 | } |
74 | 77 | ||
diff --git a/include/linux/mount.h b/include/linux/mount.h index f0dfb17ffccc..b4836d58f428 100644 --- a/include/linux/mount.h +++ b/include/linux/mount.h | |||
@@ -57,6 +57,7 @@ struct vfsmount { | |||
57 | struct vfsmount *mnt_master; /* slave is on master->mnt_slave_list */ | 57 | struct vfsmount *mnt_master; /* slave is on master->mnt_slave_list */ |
58 | struct mnt_namespace *mnt_ns; /* containing namespace */ | 58 | struct mnt_namespace *mnt_ns; /* containing namespace */ |
59 | int mnt_id; /* mount identifier */ | 59 | int mnt_id; /* mount identifier */ |
60 | int mnt_group_id; /* peer group identifier */ | ||
60 | /* | 61 | /* |
61 | * We put mnt_count & mnt_expiry_mark at the end of struct vfsmount | 62 | * We put mnt_count & mnt_expiry_mark at the end of struct vfsmount |
62 | * to let these frequently modified fields in a separate cache line | 63 | * to let these frequently modified fields in a separate cache line |