arm64: dts: rockchip: fix sdmmc1_bus4 pinctrl for rk3328
[firefly-linux-kernel-4.4.55.git] / fs / pnode.c
index 6367e1e435c64144a1d9adb94e9c1cfd1f157dfe..1d16bb3bdf5ef0a77518a24eae8bf3b945dfde88 100644 (file)
@@ -198,10 +198,15 @@ static struct mount *next_group(struct mount *m, struct mount *origin)
 
 /* all accesses are serialized by namespace_sem */
 static struct user_namespace *user_ns;
-static struct mount *last_dest, *last_source, *dest_master;
+static struct mount *last_dest, *first_source, *last_source, *dest_master;
 static struct mountpoint *mp;
 static struct hlist_head *list;
 
+static inline bool peers(struct mount *m1, struct mount *m2)
+{
+       return m1->mnt_group_id == m2->mnt_group_id && m1->mnt_group_id;
+}
+
 static int propagate_one(struct mount *m)
 {
        struct mount *child;
@@ -212,24 +217,26 @@ static int propagate_one(struct mount *m)
        /* skip if mountpoint isn't covered by it */
        if (!is_subdir(mp->m_dentry, m->mnt.mnt_root))
                return 0;
-       if (m->mnt_group_id == last_dest->mnt_group_id) {
+       if (peers(m, last_dest)) {
                type = CL_MAKE_SHARED;
        } else {
                struct mount *n, *p;
+               bool done;
                for (n = m; ; n = p) {
                        p = n->mnt_master;
-                       if (p == dest_master || IS_MNT_MARKED(p)) {
-                               while (last_dest->mnt_master != p) {
-                                       last_source = last_source->mnt_master;
-                                       last_dest = last_source->mnt_parent;
-                               }
-                               if (n->mnt_group_id != last_dest->mnt_group_id) {
-                                       last_source = last_source->mnt_master;
-                                       last_dest = last_source->mnt_parent;
-                               }
+                       if (p == dest_master || IS_MNT_MARKED(p))
                                break;
-                       }
                }
+               do {
+                       struct mount *parent = last_source->mnt_parent;
+                       if (last_source == first_source)
+                               break;
+                       done = parent->mnt_master == p;
+                       if (done && peers(n, parent))
+                               break;
+                       last_source = last_source->mnt_master;
+               } while (!done);
+
                type = CL_SLAVE;
                /* beginning of peer group among the slaves? */
                if (IS_MNT_SHARED(m))
@@ -281,6 +288,7 @@ int propagate_mnt(struct mount *dest_mnt, struct mountpoint *dest_mp,
         */
        user_ns = current->nsproxy->mnt_ns->user_ns;
        last_dest = dest_mnt;
+       first_source = source_mnt;
        last_source = source_mnt;
        mp = dest_mp;
        list = tree_list;
@@ -316,6 +324,21 @@ out:
        return ret;
 }
 
+static struct mount *find_topper(struct mount *mnt)
+{
+       /* If there is exactly one mount covering mnt completely return it. */
+       struct mount *child;
+
+       if (!list_is_singular(&mnt->mnt_mounts))
+               return NULL;
+
+       child = list_first_entry(&mnt->mnt_mounts, struct mount, mnt_child);
+       if (child->mnt_mountpoint != mnt->mnt.mnt_root)
+               return NULL;
+
+       return child;
+}
+
 /*
  * return true if the refcount is greater than count
  */
@@ -336,9 +359,8 @@ static inline int do_refcount_check(struct mount *mnt, int count)
  */
 int propagate_mount_busy(struct mount *mnt, int refcnt)
 {
-       struct mount *m, *child;
+       struct mount *m, *child, *topper;
        struct mount *parent = mnt->mnt_parent;
-       int ret = 0;
 
        if (mnt == parent)
                return do_refcount_check(mnt, refcnt);
@@ -353,12 +375,24 @@ int propagate_mount_busy(struct mount *mnt, int refcnt)
 
        for (m = propagation_next(parent, parent); m;
                        m = propagation_next(m, parent)) {
-               child = __lookup_mnt_last(&m->mnt, mnt->mnt_mountpoint);
-               if (child && list_empty(&child->mnt_mounts) &&
-                   (ret = do_refcount_check(child, 1)))
-                       break;
+               int count = 1;
+               child = __lookup_mnt(&m->mnt, mnt->mnt_mountpoint);
+               if (!child)
+                       continue;
+
+               /* Is there exactly one mount on the child that covers
+                * it completely whose reference should be ignored?
+                */
+               topper = find_topper(child);
+               if (topper)
+                       count += 1;
+               else if (!list_empty(&child->mnt_mounts))
+                       continue;
+
+               if (do_refcount_check(child, count))
+                       return 1;
        }
-       return ret;
+       return 0;
 }
 
 /*
@@ -375,7 +409,7 @@ void propagate_mount_unlock(struct mount *mnt)
 
        for (m = propagation_next(parent, parent); m;
                        m = propagation_next(m, parent)) {
-               child = __lookup_mnt_last(&m->mnt, mnt->mnt_mountpoint);
+               child = __lookup_mnt(&m->mnt, mnt->mnt_mountpoint);
                if (child)
                        child->mnt.mnt_flags &= ~MNT_LOCKED;
        }
@@ -393,9 +427,11 @@ static void mark_umount_candidates(struct mount *mnt)
 
        for (m = propagation_next(parent, parent); m;
                        m = propagation_next(m, parent)) {
-               struct mount *child = __lookup_mnt_last(&m->mnt,
+               struct mount *child = __lookup_mnt(&m->mnt,
                                                mnt->mnt_mountpoint);
-               if (child && (!IS_MNT_LOCKED(child) || IS_MNT_MARKED(m))) {
+               if (!child || (child->mnt.mnt_flags & MNT_UMOUNT))
+                       continue;
+               if (!IS_MNT_LOCKED(child) || IS_MNT_MARKED(m)) {
                        SET_MNT_MARK(child);
                }
        }
@@ -414,8 +450,8 @@ static void __propagate_umount(struct mount *mnt)
 
        for (m = propagation_next(parent, parent); m;
                        m = propagation_next(m, parent)) {
-
-               struct mount *child = __lookup_mnt_last(&m->mnt,
+               struct mount *topper;
+               struct mount *child = __lookup_mnt(&m->mnt,
                                                mnt->mnt_mountpoint);
                /*
                 * umount the child only if the child has no children
@@ -424,6 +460,15 @@ static void __propagate_umount(struct mount *mnt)
                if (!child || !IS_MNT_MARKED(child))
                        continue;
                CLEAR_MNT_MARK(child);
+
+               /* If there is exactly one mount covering all of child
+                * replace child with that mount.
+                */
+               topper = find_topper(child);
+               if (topper)
+                       mnt_change_mountpoint(child->mnt_parent, child->mnt_mp,
+                                             topper);
+
                if (list_empty(&child->mnt_mounts)) {
                        list_del_init(&child->mnt_child);
                        child->mnt.mnt_flags |= MNT_UMOUNT;
@@ -450,3 +495,32 @@ int propagate_umount(struct list_head *list)
                __propagate_umount(mnt);
        return 0;
 }
+
+/*
+ *  Iterates over all slaves, and slaves of slaves.
+ */
+static struct mount *next_descendent(struct mount *root, struct mount *cur)
+{
+       if (!IS_MNT_NEW(cur) && !list_empty(&cur->mnt_slave_list))
+               return first_slave(cur);
+       do {
+               if (cur->mnt_slave.next != &cur->mnt_master->mnt_slave_list)
+                       return next_slave(cur);
+               cur = cur->mnt_master;
+       } while (cur != root);
+       return NULL;
+}
+
+void propagate_remount(struct mount *mnt)
+{
+       struct mount *m = mnt;
+       struct super_block *sb = mnt->mnt.mnt_sb;
+
+       if (sb->s_op->copy_mnt_data) {
+               m = next_descendent(mnt, m);
+               while (m) {
+                       sb->s_op->copy_mnt_data(m->mnt.data, mnt->mnt.data);
+                       m = next_descendent(mnt, m);
+               }
+       }
+}