OSDN Git Service

btrfs: set trans->drity in btrfs_commit_transaction
[sagit-ice-cold/kernel_xiaomi_msm8998.git] / fs / pnode.c
index acc2eef..d15c63e 100644 (file)
@@ -24,6 +24,11 @@ static inline struct mount *first_slave(struct mount *p)
        return list_entry(p->mnt_slave_list.next, struct mount, mnt_slave);
 }
 
+static inline struct mount *last_slave(struct mount *p)
+{
+       return list_entry(p->mnt_slave_list.prev, struct mount, mnt_slave);
+}
+
 static inline struct mount *next_slave(struct mount *p)
 {
        return list_entry(p->mnt_slave.next, struct mount, mnt_slave);
@@ -164,6 +169,19 @@ static struct mount *propagation_next(struct mount *m,
        }
 }
 
+static struct mount *skip_propagation_subtree(struct mount *m,
+                                               struct mount *origin)
+{
+       /*
+        * Advance m such that propagation_next will not return
+        * the slaves of m.
+        */
+       if (!IS_MNT_NEW(m) && !list_empty(&m->mnt_slave_list))
+               m = last_slave(m);
+
+       return m;
+}
+
 static struct mount *next_group(struct mount *m, struct mount *origin)
 {
        while (1) {
@@ -507,6 +525,15 @@ static void restore_mounts(struct list_head *to_restore)
        }
 }
 
+static void cleanup_umount_visitations(struct list_head *visited)
+{
+       while (!list_empty(visited)) {
+               struct mount *mnt =
+                       list_first_entry(visited, struct mount, mnt_umounting);
+               list_del_init(&mnt->mnt_umounting);
+       }
+}
+
 /*
  * collect all mounts that receive propagation from the mount in @list,
  * and return these additional mounts in the same list.
@@ -519,11 +546,23 @@ int propagate_umount(struct list_head *list)
        struct mount *mnt;
        LIST_HEAD(to_restore);
        LIST_HEAD(to_umount);
+       LIST_HEAD(visited);
 
-       list_for_each_entry(mnt, list, mnt_list) {
+       /* Find candidates for unmounting */
+       list_for_each_entry_reverse(mnt, list, mnt_list) {
                struct mount *parent = mnt->mnt_parent;
                struct mount *m;
 
+               /*
+                * If this mount has already been visited it is known that it's
+                * entire peer group and all of their slaves in the propagation
+                * tree for the mountpoint has already been visited and there is
+                * no need to visit them again.
+                */
+               if (!list_empty(&mnt->mnt_umounting))
+                       continue;
+
+               list_add_tail(&mnt->mnt_umounting, &visited);
                for (m = propagation_next(parent, parent); m;
                     m = propagation_next(m, parent)) {
                        struct mount *child = __lookup_mnt(&m->mnt,
@@ -531,6 +570,27 @@ int propagate_umount(struct list_head *list)
                        if (!child)
                                continue;
 
+                       if (!list_empty(&child->mnt_umounting)) {
+                               /*
+                                * If the child has already been visited it is
+                                * know that it's entire peer group and all of
+                                * their slaves in the propgation tree for the
+                                * mountpoint has already been visited and there
+                                * is no need to visit this subtree again.
+                                */
+                               m = skip_propagation_subtree(m, parent);
+                               continue;
+                       } else if (child->mnt.mnt_flags & MNT_UMOUNT) {
+                               /*
+                                * We have come accross an partially unmounted
+                                * mount in list that has not been visited yet.
+                                * Remember it has been visited and continue
+                                * about our merry way.
+                                */
+                               list_add_tail(&child->mnt_umounting, &visited);
+                               continue;
+                       }
+
                        /* Check the child and parents while progress is made */
                        while (__propagate_umount(child,
                                                  &to_umount, &to_restore)) {
@@ -544,6 +604,7 @@ int propagate_umount(struct list_head *list)
 
        umount_list(&to_umount, &to_restore);
        restore_mounts(&to_restore);
+       cleanup_umount_visitations(&visited);
        list_splice_tail(&to_umount, list);
 
        return 0;