diff options
author | Trond Myklebust <Trond.Myklebust@netapp.com> | 2006-06-09 09:34:17 -0400 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2006-06-09 09:34:17 -0400 |
commit | 5528f911b4c43a5de5da34bcbd7e3f2a62503617 (patch) | |
tree | 2c888b1dc0b3743855d795a1a02730637d6ba0aa | |
parent | 1f5ce9e93aa96a867f195ed45f6f77935175f12e (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.c | 124 | ||||
-rw-r--r-- | include/linux/mount.h | 3 |
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 | */ | ||
1171 | static 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 | */ |
1170 | void mark_mounts_for_expiry(struct list_head *mounts) | 1204 | void 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) | 1234 | EXPORT_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 | */ | ||
1242 | static 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 | |||
1248 | repeat: | ||
1249 | next = this_parent->mnt_mounts.next; | ||
1250 | resume: | ||
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 | */ | ||
1287 | void 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 | ||
1227 | EXPORT_SYMBOL_GPL(mark_mounts_for_expiry); | 1301 | EXPORT_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 | ||
86 | extern void mark_mounts_for_expiry(struct list_head *mounts); | 88 | extern void mark_mounts_for_expiry(struct list_head *mounts); |
89 | extern void shrink_submounts(struct vfsmount *mountpoint, struct list_head *mounts); | ||
87 | 90 | ||
88 | extern spinlock_t vfsmount_lock; | 91 | extern spinlock_t vfsmount_lock; |
89 | extern dev_t name_to_dev_t(char *name); | 92 | extern dev_t name_to_dev_t(char *name); |