diff options
Diffstat (limited to 'fs/pnode.c')
-rw-r--r-- | fs/pnode.c | 60 |
1 files changed, 53 insertions, 7 deletions
diff --git a/fs/pnode.c b/fs/pnode.c index 260ac8f898a4..6367e1e435c6 100644 --- a/fs/pnode.c +++ b/fs/pnode.c | |||
@@ -362,6 +362,46 @@ int propagate_mount_busy(struct mount *mnt, int refcnt) | |||
362 | } | 362 | } |
363 | 363 | ||
364 | /* | 364 | /* |
365 | * Clear MNT_LOCKED when it can be shown to be safe. | ||
366 | * | ||
367 | * mount_lock lock must be held for write | ||
368 | */ | ||
369 | void propagate_mount_unlock(struct mount *mnt) | ||
370 | { | ||
371 | struct mount *parent = mnt->mnt_parent; | ||
372 | struct mount *m, *child; | ||
373 | |||
374 | BUG_ON(parent == mnt); | ||
375 | |||
376 | for (m = propagation_next(parent, parent); m; | ||
377 | m = propagation_next(m, parent)) { | ||
378 | child = __lookup_mnt_last(&m->mnt, mnt->mnt_mountpoint); | ||
379 | if (child) | ||
380 | child->mnt.mnt_flags &= ~MNT_LOCKED; | ||
381 | } | ||
382 | } | ||
383 | |||
384 | /* | ||
385 | * Mark all mounts that the MNT_LOCKED logic will allow to be unmounted. | ||
386 | */ | ||
387 | static void mark_umount_candidates(struct mount *mnt) | ||
388 | { | ||
389 | struct mount *parent = mnt->mnt_parent; | ||
390 | struct mount *m; | ||
391 | |||
392 | BUG_ON(parent == mnt); | ||
393 | |||
394 | for (m = propagation_next(parent, parent); m; | ||
395 | m = propagation_next(m, parent)) { | ||
396 | struct mount *child = __lookup_mnt_last(&m->mnt, | ||
397 | mnt->mnt_mountpoint); | ||
398 | if (child && (!IS_MNT_LOCKED(child) || IS_MNT_MARKED(m))) { | ||
399 | SET_MNT_MARK(child); | ||
400 | } | ||
401 | } | ||
402 | } | ||
403 | |||
404 | /* | ||
365 | * NOTE: unmounting 'mnt' naturally propagates to all other mounts its | 405 | * NOTE: unmounting 'mnt' naturally propagates to all other mounts its |
366 | * parent propagates to. | 406 | * parent propagates to. |
367 | */ | 407 | */ |
@@ -378,13 +418,16 @@ static void __propagate_umount(struct mount *mnt) | |||
378 | struct mount *child = __lookup_mnt_last(&m->mnt, | 418 | struct mount *child = __lookup_mnt_last(&m->mnt, |
379 | mnt->mnt_mountpoint); | 419 | mnt->mnt_mountpoint); |
380 | /* | 420 | /* |
381 | * umount the child only if the child has no | 421 | * umount the child only if the child has no children |
382 | * other children | 422 | * and the child is marked safe to unmount. |
383 | */ | 423 | */ |
384 | if (child && list_empty(&child->mnt_mounts)) { | 424 | if (!child || !IS_MNT_MARKED(child)) |
425 | continue; | ||
426 | CLEAR_MNT_MARK(child); | ||
427 | if (list_empty(&child->mnt_mounts)) { | ||
385 | list_del_init(&child->mnt_child); | 428 | list_del_init(&child->mnt_child); |
386 | hlist_del_init_rcu(&child->mnt_hash); | 429 | child->mnt.mnt_flags |= MNT_UMOUNT; |
387 | hlist_add_before_rcu(&child->mnt_hash, &mnt->mnt_hash); | 430 | list_move_tail(&child->mnt_list, &mnt->mnt_list); |
388 | } | 431 | } |
389 | } | 432 | } |
390 | } | 433 | } |
@@ -396,11 +439,14 @@ static void __propagate_umount(struct mount *mnt) | |||
396 | * | 439 | * |
397 | * vfsmount lock must be held for write | 440 | * vfsmount lock must be held for write |
398 | */ | 441 | */ |
399 | int propagate_umount(struct hlist_head *list) | 442 | int propagate_umount(struct list_head *list) |
400 | { | 443 | { |
401 | struct mount *mnt; | 444 | struct mount *mnt; |
402 | 445 | ||
403 | hlist_for_each_entry(mnt, list, mnt_hash) | 446 | list_for_each_entry_reverse(mnt, list, mnt_list) |
447 | mark_umount_candidates(mnt); | ||
448 | |||
449 | list_for_each_entry(mnt, list, mnt_list) | ||
404 | __propagate_umount(mnt); | 450 | __propagate_umount(mnt); |
405 | return 0; | 451 | return 0; |
406 | } | 452 | } |