OSDN Git Service

h8300: Add missing symbol "BOOT_LINK_OFFSET"
[uclinux-h8/linux.git] / fs / namespace.c
index 0044fee..6e9844b 100644 (file)
@@ -344,8 +344,24 @@ int __mnt_want_write(struct vfsmount *m)
         * incremented count after it has set MNT_WRITE_HOLD.
         */
        smp_mb();
-       while (READ_ONCE(mnt->mnt.mnt_flags) & MNT_WRITE_HOLD)
-               cpu_relax();
+       might_lock(&mount_lock.lock);
+       while (READ_ONCE(mnt->mnt.mnt_flags) & MNT_WRITE_HOLD) {
+               if (!IS_ENABLED(CONFIG_PREEMPT_RT)) {
+                       cpu_relax();
+               } else {
+                       /*
+                        * This prevents priority inversion, if the task
+                        * setting MNT_WRITE_HOLD got preempted on a remote
+                        * CPU, and it prevents life lock if the task setting
+                        * MNT_WRITE_HOLD has a lower priority and is bound to
+                        * the same CPU as the task that is spinning here.
+                        */
+                       preempt_enable();
+                       lock_mount_hash();
+                       unlock_mount_hash();
+                       preempt_disable();
+               }
+       }
        /*
         * After the slowpath clears MNT_WRITE_HOLD, mnt_is_readonly will
         * be set to match its requirements. So we must not load that until
@@ -563,12 +579,9 @@ int sb_prepare_remount_readonly(struct super_block *sb)
        lock_mount_hash();
        list_for_each_entry(mnt, &sb->s_mounts, mnt_instance) {
                if (!(mnt->mnt.mnt_flags & MNT_READONLY)) {
-                       mnt->mnt.mnt_flags |= MNT_WRITE_HOLD;
-                       smp_mb();
-                       if (mnt_get_writers(mnt) > 0) {
-                               err = -EBUSY;
+                       err = mnt_hold_writers(mnt);
+                       if (err)
                                break;
-                       }
                }
        }
        if (!err && atomic_long_read(&sb->s_remove_count))
@@ -4000,46 +4013,57 @@ static int can_idmap_mount(const struct mount_kattr *kattr, struct mount *mnt)
        return 0;
 }
 
-static struct mount *mount_setattr_prepare(struct mount_kattr *kattr,
-                                          struct mount *mnt, int *err)
+/**
+ * mnt_allow_writers() - check whether the attribute change allows writers
+ * @kattr: the new mount attributes
+ * @mnt: the mount to which @kattr will be applied
+ *
+ * Check whether thew new mount attributes in @kattr allow concurrent writers.
+ *
+ * Return: true if writers need to be held, false if not
+ */
+static inline bool mnt_allow_writers(const struct mount_kattr *kattr,
+                                    const struct mount *mnt)
 {
-       struct mount *m = mnt, *last = NULL;
+       return !(kattr->attr_set & MNT_READONLY) ||
+              (mnt->mnt.mnt_flags & MNT_READONLY);
+}
 
-       if (!is_mounted(&m->mnt)) {
-               *err = -EINVAL;
-               goto out;
-       }
+static int mount_setattr_prepare(struct mount_kattr *kattr, struct mount *mnt)
+{
+       struct mount *m;
+       int err;
 
-       if (!(mnt_has_parent(m) ? check_mnt(m) : is_anon_ns(m->mnt_ns))) {
-               *err = -EINVAL;
-               goto out;
-       }
+       for (m = mnt; m; m = next_mnt(m, mnt)) {
+               if (!can_change_locked_flags(m, recalc_flags(kattr, m))) {
+                       err = -EPERM;
+                       break;
+               }
 
-       do {
-               unsigned int flags;
+               err = can_idmap_mount(kattr, m);
+               if (err)
+                       break;
 
-               flags = recalc_flags(kattr, m);
-               if (!can_change_locked_flags(m, flags)) {
-                       *err = -EPERM;
-                       goto out;
+               if (!mnt_allow_writers(kattr, m)) {
+                       err = mnt_hold_writers(m);
+                       if (err)
+                               break;
                }
 
-               *err = can_idmap_mount(kattr, m);
-               if (*err)
-                       goto out;
+               if (!kattr->recurse)
+                       return 0;
+       }
 
-               last = m;
+       if (err) {
+               struct mount *p;
 
-               if ((kattr->attr_set & MNT_READONLY) &&
-                   !(m->mnt.mnt_flags & MNT_READONLY)) {
-                       *err = mnt_hold_writers(m);
-                       if (*err)
-                               goto out;
+               for (p = mnt; p != m; p = next_mnt(p, mnt)) {
+                       /* If we had to hold writers unblock them. */
+                       if (p->mnt.mnt_flags & MNT_WRITE_HOLD)
+                               mnt_unhold_writers(p);
                }
-       } while (kattr->recurse && (m = next_mnt(m, mnt)));
-
-out:
-       return last;
+       }
+       return err;
 }
 
 static void do_idmap_mount(const struct mount_kattr *kattr, struct mount *mnt)
@@ -4067,48 +4091,32 @@ static void do_idmap_mount(const struct mount_kattr *kattr, struct mount *mnt)
                put_user_ns(old_mnt_userns);
 }
 
-static void mount_setattr_commit(struct mount_kattr *kattr,
-                                struct mount *mnt, struct mount *last,
-                                int err)
+static void mount_setattr_commit(struct mount_kattr *kattr, struct mount *mnt)
 {
-       struct mount *m = mnt;
+       struct mount *m;
 
-       do {
-               if (!err) {
-                       unsigned int flags;
+       for (m = mnt; m; m = next_mnt(m, mnt)) {
+               unsigned int flags;
 
-                       do_idmap_mount(kattr, m);
-                       flags = recalc_flags(kattr, m);
-                       WRITE_ONCE(m->mnt.mnt_flags, flags);
-               }
+               do_idmap_mount(kattr, m);
+               flags = recalc_flags(kattr, m);
+               WRITE_ONCE(m->mnt.mnt_flags, flags);
 
-               /*
-                * We either set MNT_READONLY above so make it visible
-                * before ~MNT_WRITE_HOLD or we failed to recursively
-                * apply mount options.
-                */
-               if ((kattr->attr_set & MNT_READONLY) &&
-                   (m->mnt.mnt_flags & MNT_WRITE_HOLD))
+               /* If we had to hold writers unblock them. */
+               if (m->mnt.mnt_flags & MNT_WRITE_HOLD)
                        mnt_unhold_writers(m);
 
-               if (!err && kattr->propagation)
+               if (kattr->propagation)
                        change_mnt_propagation(m, kattr->propagation);
-
-               /*
-                * On failure, only cleanup until we found the first mount
-                * we failed to handle.
-                */
-               if (err && m == last)
+               if (!kattr->recurse)
                        break;
-       } while (kattr->recurse && (m = next_mnt(m, mnt)));
-
-       if (!err)
-               touch_mnt_namespace(mnt->mnt_ns);
+       }
+       touch_mnt_namespace(mnt->mnt_ns);
 }
 
 static int do_mount_setattr(struct path *path, struct mount_kattr *kattr)
 {
-       struct mount *mnt = real_mount(path->mnt), *last = NULL;
+       struct mount *mnt = real_mount(path->mnt);
        int err = 0;
 
        if (path->dentry != mnt->mnt.mnt_root)
@@ -4129,16 +4137,32 @@ static int do_mount_setattr(struct path *path, struct mount_kattr *kattr)
                }
        }
 
+       err = -EINVAL;
        lock_mount_hash();
 
+       /* Ensure that this isn't anything purely vfs internal. */
+       if (!is_mounted(&mnt->mnt))
+               goto out;
+
        /*
-        * Get the mount tree in a shape where we can change mount
-        * properties without failure.
+        * If this is an attached mount make sure it's located in the callers
+        * mount namespace. If it's not don't let the caller interact with it.
+        * If this is a detached mount make sure it has an anonymous mount
+        * namespace attached to it, i.e. we've created it via OPEN_TREE_CLONE.
         */
-       last = mount_setattr_prepare(kattr, mnt, &err);
-       if (last) /* Commit all changes or revert to the old state. */
-               mount_setattr_commit(kattr, mnt, last, err);
+       if (!(mnt_has_parent(mnt) ? check_mnt(mnt) : is_anon_ns(mnt->mnt_ns)))
+               goto out;
 
+       /*
+        * First, we get the mount tree in a shape where we can change mount
+        * properties without failure. If we succeeded to do so we commit all
+        * changes and if we failed we clean up.
+        */
+       err = mount_setattr_prepare(kattr, mnt);
+       if (!err)
+               mount_setattr_commit(kattr, mnt);
+
+out:
        unlock_mount_hash();
 
        if (kattr->propagation) {