aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTrond Myklebust <Trond.Myklebust@netapp.com>2006-06-09 09:34:17 -0400
committerTrond Myklebust <Trond.Myklebust@netapp.com>2006-06-09 09:34:17 -0400
commit5528f911b4c43a5de5da34bcbd7e3f2a62503617 (patch)
tree2c888b1dc0b3743855d795a1a02730637d6ba0aa
parent1f5ce9e93aa96a867f195ed45f6f77935175f12e (diff)
VFS: Add shrink_submounts()
Allow a submount to be marked as being 'shrinkable' by means of the vfsmount->mnt_flags, and then add a function 'shrink_submounts()' which attempts to recursively unmount these submounts. Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
-rw-r--r--fs/namespace.c124
-rw-r--r--include/linux/mount.h3
2 files changed, 102 insertions, 25 deletions
diff --git a/fs/namespace.c b/fs/namespace.c
index bf478addb852..b22e469ab560 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -1163,13 +1163,46 @@ static void expire_mount(struct vfsmount *mnt, struct list_head *mounts,
1163} 1163}
1164 1164
1165/* 1165/*
1166 * go through the vfsmounts we've just consigned to the graveyard to
1167 * - check that they're still dead
1168 * - delete the vfsmount from the appropriate namespace under lock
1169 * - dispose of the corpse
1170 */
1171static void expire_mount_list(struct list_head *graveyard, struct list_head *mounts)
1172{
1173 struct namespace *namespace;
1174 struct vfsmount *mnt;
1175
1176 while (!list_empty(graveyard)) {
1177 LIST_HEAD(umounts);
1178 mnt = list_entry(graveyard->next, struct vfsmount, mnt_expire);
1179 list_del_init(&mnt->mnt_expire);
1180
1181 /* don't do anything if the namespace is dead - all the
1182 * vfsmounts from it are going away anyway */
1183 namespace = mnt->mnt_namespace;
1184 if (!namespace || !namespace->root)
1185 continue;
1186 get_namespace(namespace);
1187
1188 spin_unlock(&vfsmount_lock);
1189 down_write(&namespace_sem);
1190 expire_mount(mnt, mounts, &umounts);
1191 up_write(&namespace_sem);
1192 release_mounts(&umounts);
1193 mntput(mnt);
1194 put_namespace(namespace);
1195 spin_lock(&vfsmount_lock);
1196 }
1197}
1198
1199/*
1166 * process a list of expirable mountpoints with the intent of discarding any 1200 * process a list of expirable mountpoints with the intent of discarding any
1167 * mountpoints that aren't in use and haven't been touched since last we came 1201 * mountpoints that aren't in use and haven't been touched since last we came
1168 * here 1202 * here
1169 */ 1203 */
1170void mark_mounts_for_expiry(struct list_head *mounts) 1204void mark_mounts_for_expiry(struct list_head *mounts)
1171{ 1205{
1172 struct namespace *namespace;
1173 struct vfsmount *mnt, *next; 1206 struct vfsmount *mnt, *next;
1174 LIST_HEAD(graveyard); 1207 LIST_HEAD(graveyard);
1175 1208
@@ -1193,38 +1226,79 @@ void mark_mounts_for_expiry(struct list_head *mounts)
1193 list_move(&mnt->mnt_expire, &graveyard); 1226 list_move(&mnt->mnt_expire, &graveyard);
1194 } 1227 }
1195 1228
1196 /* 1229 expire_mount_list(&graveyard, mounts);
1197 * go through the vfsmounts we've just consigned to the graveyard to
1198 * - check that they're still dead
1199 * - delete the vfsmount from the appropriate namespace under lock
1200 * - dispose of the corpse
1201 */
1202 while (!list_empty(&graveyard)) {
1203 LIST_HEAD(umounts);
1204 mnt = list_entry(graveyard.next, struct vfsmount, mnt_expire);
1205 list_del_init(&mnt->mnt_expire);
1206 1230
1207 /* don't do anything if the namespace is dead - all the 1231 spin_unlock(&vfsmount_lock);
1208 * vfsmounts from it are going away anyway */ 1232}
1209 namespace = mnt->mnt_namespace; 1233
1210 if (!namespace || !namespace->root) 1234EXPORT_SYMBOL_GPL(mark_mounts_for_expiry);
1235
1236/*
1237 * Ripoff of 'select_parent()'
1238 *
1239 * search the list of submounts for a given mountpoint, and move any
1240 * shrinkable submounts to the 'graveyard' list.
1241 */
1242static int select_submounts(struct vfsmount *parent, struct list_head *graveyard)
1243{
1244 struct vfsmount *this_parent = parent;
1245 struct list_head *next;
1246 int found = 0;
1247
1248repeat:
1249 next = this_parent->mnt_mounts.next;
1250resume:
1251 while (next != &this_parent->mnt_mounts) {
1252 struct list_head *tmp = next;
1253 struct vfsmount *mnt = list_entry(tmp, struct vfsmount, mnt_child);
1254
1255 next = tmp->next;
1256 if (!(mnt->mnt_flags & MNT_SHRINKABLE))
1211 continue; 1257 continue;
1212 get_namespace(namespace); 1258 /*
1259 * Descend a level if the d_mounts list is non-empty.
1260 */
1261 if (!list_empty(&mnt->mnt_mounts)) {
1262 this_parent = mnt;
1263 goto repeat;
1264 }
1213 1265
1214 spin_unlock(&vfsmount_lock); 1266 if (!propagate_mount_busy(mnt, 1)) {
1215 down_write(&namespace_sem); 1267 mntget(mnt);
1216 expire_mount(mnt, mounts, &umounts); 1268 list_move_tail(&mnt->mnt_expire, graveyard);
1217 up_write(&namespace_sem); 1269 found++;
1218 release_mounts(&umounts); 1270 }
1219 mntput(mnt);
1220 put_namespace(namespace);
1221 spin_lock(&vfsmount_lock);
1222 } 1271 }
1272 /*
1273 * All done at this level ... ascend and resume the search
1274 */
1275 if (this_parent != parent) {
1276 next = this_parent->mnt_child.next;
1277 this_parent = this_parent->mnt_parent;
1278 goto resume;
1279 }
1280 return found;
1281}
1282
1283/*
1284 * process a list of expirable mountpoints with the intent of discarding any
1285 * submounts of a specific parent mountpoint
1286 */
1287void shrink_submounts(struct vfsmount *mountpoint, struct list_head *mounts)
1288{
1289 LIST_HEAD(graveyard);
1290 int found;
1291
1292 spin_lock(&vfsmount_lock);
1293
1294 /* extract submounts of 'mountpoint' from the expiration list */
1295 while ((found = select_submounts(mountpoint, &graveyard)) != 0)
1296 expire_mount_list(&graveyard, mounts);
1223 1297
1224 spin_unlock(&vfsmount_lock); 1298 spin_unlock(&vfsmount_lock);
1225} 1299}
1226 1300
1227EXPORT_SYMBOL_GPL(mark_mounts_for_expiry); 1301EXPORT_SYMBOL_GPL(shrink_submounts);
1228 1302
1229/* 1303/*
1230 * Some copy_from_user() implementations do not return the exact number of 1304 * Some copy_from_user() implementations do not return the exact number of
diff --git a/include/linux/mount.h b/include/linux/mount.h
index aff68c3660f5..9b4e0071b92e 100644
--- a/include/linux/mount.h
+++ b/include/linux/mount.h
@@ -23,6 +23,8 @@
23#define MNT_NOATIME 0x08 23#define MNT_NOATIME 0x08
24#define MNT_NODIRATIME 0x10 24#define MNT_NODIRATIME 0x10
25 25
26#define MNT_SHRINKABLE 0x100
27
26#define MNT_SHARED 0x1000 /* if the vfsmount is a shared mount */ 28#define MNT_SHARED 0x1000 /* if the vfsmount is a shared mount */
27#define MNT_UNBINDABLE 0x2000 /* if the vfsmount is a unbindable mount */ 29#define MNT_UNBINDABLE 0x2000 /* if the vfsmount is a unbindable mount */
28#define MNT_PNODE_MASK 0x3000 /* propogation flag mask */ 30#define MNT_PNODE_MASK 0x3000 /* propogation flag mask */
@@ -84,6 +86,7 @@ extern int do_add_mount(struct vfsmount *newmnt, struct nameidata *nd,
84 int mnt_flags, struct list_head *fslist); 86 int mnt_flags, struct list_head *fslist);
85 87
86extern void mark_mounts_for_expiry(struct list_head *mounts); 88extern void mark_mounts_for_expiry(struct list_head *mounts);
89extern void shrink_submounts(struct vfsmount *mountpoint, struct list_head *mounts);
87 90
88extern spinlock_t vfsmount_lock; 91extern spinlock_t vfsmount_lock;
89extern dev_t name_to_dev_t(char *name); 92extern dev_t name_to_dev_t(char *name);