Commit 719f5d7f0b90ac2c8f8ca4232eb322b266fea01e

Authored by Miklos Szeredi
Committed by Al Viro
1 parent 73cd49ecdd

[patch 4/7] vfs: mountinfo: add mount peer group ID

Add a unique ID to each peer group using the IDR infrastructure.  The
identifiers are reused after the peer group dissolves.

The IDR structures are protected by holding namepspace_sem for write
while allocating or deallocating IDs.

IDs are allocated when a previously unshared vfsmount becomes the
first member of a peer group.  When a new member is added to an
existing group, the ID is copied from one of the old members.

IDs are freed when the last member of a peer group is unshared.

Setting the MNT_SHARED flag on members of a subtree is done as a
separate step, after all the IDs have been allocated.  This way an
allocation failure can be cleaned up easilty, without affecting the
propagation state.

Based on design sketch by Al Viro.

Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>

Showing 3 changed files with 95 additions and 4 deletions Side-by-side Diff

... ... @@ -41,6 +41,7 @@
41 41  
42 42 static int event;
43 43 static DEFINE_IDA(mnt_id_ida);
  44 +static DEFINE_IDA(mnt_group_ida);
44 45  
45 46 static struct list_head *mount_hashtable __read_mostly;
46 47 static struct kmem_cache *mnt_cache __read_mostly;
... ... @@ -83,6 +84,28 @@
83 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 109 struct vfsmount *alloc_vfsmnt(const char *name)
87 110 {
88 111 struct vfsmount *mnt = kmem_cache_zalloc(mnt_cache, GFP_KERNEL);
... ... @@ -533,6 +556,17 @@
533 556 struct vfsmount *mnt = alloc_vfsmnt(old->mnt_devname);
534 557  
535 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 570 mnt->mnt_flags = old->mnt_flags;
537 571 atomic_inc(&sb->s_active);
538 572 mnt->mnt_sb = sb;
... ... @@ -562,6 +596,10 @@
562 596 }
563 597 }
564 598 return mnt;
  599 +
  600 + out_free:
  601 + free_vfsmnt(mnt);
  602 + return NULL;
565 603 }
566 604  
567 605 static inline void __mntput(struct vfsmount *mnt)
... ... @@ -1142,6 +1180,33 @@
1142 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 1211 * @source_mnt : mount tree to be attached
1147 1212 * @nd : place the mount tree @source_mnt is attached
1148 1213  
... ... @@ -1212,9 +1277,16 @@
1212 1277 struct vfsmount *dest_mnt = path->mnt;
1213 1278 struct dentry *dest_dentry = path->dentry;
1214 1279 struct vfsmount *child, *p;
  1280 + int err;
1215 1281  
1216   - if (propagate_mnt(dest_mnt, dest_dentry, source_mnt, &tree_list))
1217   - return -EINVAL;
  1282 + if (IS_MNT_SHARED(dest_mnt)) {
  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 1291 if (IS_MNT_SHARED(dest_mnt)) {
1220 1292 for (p = source_mnt; p; p = next_mnt(p, source_mnt))
... ... @@ -1237,6 +1309,12 @@
1237 1309 }
1238 1310 spin_unlock(&vfsmount_lock);
1239 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 1320 static int graft_tree(struct vfsmount *mnt, struct path *path)
... ... @@ -1277,6 +1355,7 @@
1277 1355 struct vfsmount *m, *mnt = nd->path.mnt;
1278 1356 int recurse = flag & MS_REC;
1279 1357 int type = flag & ~MS_REC;
  1358 + int err = 0;
1280 1359  
1281 1360 if (!capable(CAP_SYS_ADMIN))
1282 1361 return -EPERM;
1283 1362  
1284 1363  
... ... @@ -1285,12 +1364,20 @@
1285 1364 return -EINVAL;
1286 1365  
1287 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 1373 spin_lock(&vfsmount_lock);
1289 1374 for (m = mnt; m; m = (recurse ? next_mnt(m, mnt) : NULL))
1290 1375 change_mnt_propagation(m, type);
1291 1376 spin_unlock(&vfsmount_lock);
  1377 +
  1378 + out_unlock:
1292 1379 up_write(&namespace_sem);
1293   - return 0;
  1380 + return err;
1294 1381 }
1295 1382  
1296 1383 /*
... ... @@ -46,7 +46,11 @@
46 46 if (peer_mnt == mnt)
47 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 52 list_del_init(&mnt->mnt_share);
  53 + mnt->mnt_group_id = 0;
50 54  
51 55 if (peer_mnt)
52 56 master = peer_mnt;
... ... @@ -68,7 +72,6 @@
68 72 }
69 73 mnt->mnt_master = master;
70 74 CLEAR_MNT_SHARED(mnt);
71   - INIT_LIST_HEAD(&mnt->mnt_slave_list);
72 75 return 0;
73 76 }
74 77  
include/linux/mount.h
... ... @@ -57,6 +57,7 @@
57 57 struct vfsmount *mnt_master; /* slave is on master->mnt_slave_list */
58 58 struct mnt_namespace *mnt_ns; /* containing namespace */
59 59 int mnt_id; /* mount identifier */
  60 + int mnt_group_id; /* peer group identifier */
60 61 /*
61 62 * We put mnt_count & mnt_expiry_mark at the end of struct vfsmount
62 63 * to let these frequently modified fields in a separate cache line