Merge branch 'work.namespace' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
authorLinus Torvalds <torvalds@linux-foundation.org>
Sat, 25 Feb 2023 03:20:07 +0000 (19:20 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Sat, 25 Feb 2023 03:20:07 +0000 (19:20 -0800)
Pull ipc namespace update from Al Viro:
 "Rik's patches reducing the amount of synchronize_rcu() triggered by
  ipc namespace destruction.

  I've some pending stuff reducing that on the normal umount side, but
  it's nowhere near ready and Rik's stuff shouldn't be held back due to
  conflicts - I'll just redo the parts of my series that stray into
  ipc/*"

* 'work.namespace' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs:
  ipc,namespace: batch free ipc_namespace structures
  ipc,namespace: make ipc namespace allocation wait for pending free

1  2 
fs/namespace.c
include/linux/mount.h
ipc/mqueue.c

diff --combined fs/namespace.c
index 5927d90e24a027cd76b1ce7f9aaa6dd136897ca3,1ad4e5acef06e957c72834922faf6e9b1d56648f..bc0f15257b49c2f00f76ba53df2af8f44c6519c0
@@@ -75,6 -75,22 +75,6 @@@ static DECLARE_RWSEM(namespace_sem)
  static HLIST_HEAD(unmounted); /* protected by namespace_sem */
  static LIST_HEAD(ex_mountpoints); /* protected by namespace_sem */
  
 -struct mnt_idmap {
 -      struct user_namespace *owner;
 -      refcount_t count;
 -};
 -
 -/*
 - * Carries the initial idmapping of 0:0:4294967295 which is an identity
 - * mapping. This means that {g,u}id 0 is mapped to {g,u}id 0, {g,u}id 1 is
 - * mapped to {g,u}id 1, [...], {g,u}id 1000 to {g,u}id 1000, [...].
 - */
 -struct mnt_idmap nop_mnt_idmap = {
 -      .owner  = &init_user_ns,
 -      .count  = REFCOUNT_INIT(1),
 -};
 -EXPORT_SYMBOL_GPL(nop_mnt_idmap);
 -
  struct mount_kattr {
        unsigned int attr_set;
        unsigned int attr_clr;
@@@ -194,6 -210,104 +194,6 @@@ int mnt_get_count(struct mount *mnt
  #endif
  }
  
 -/**
 - * mnt_idmap_owner - retrieve owner of the mount's idmapping
 - * @idmap: mount idmapping
 - *
 - * This helper will go away once the conversion to use struct mnt_idmap
 - * everywhere has finished at which point the helper will be unexported.
 - *
 - * Only code that needs to perform permission checks based on the owner of the
 - * idmapping will get access to it. All other code will solely rely on
 - * idmappings. This will get us type safety so it's impossible to conflate
 - * filesystems idmappings with mount idmappings.
 - *
 - * Return: The owner of the idmapping.
 - */
 -struct user_namespace *mnt_idmap_owner(const struct mnt_idmap *idmap)
 -{
 -      return idmap->owner;
 -}
 -EXPORT_SYMBOL_GPL(mnt_idmap_owner);
 -
 -/**
 - * mnt_user_ns - retrieve owner of an idmapped mount
 - * @mnt: the relevant vfsmount
 - *
 - * This helper will go away once the conversion to use struct mnt_idmap
 - * everywhere has finished at which point the helper will be unexported.
 - *
 - * Only code that needs to perform permission checks based on the owner of the
 - * idmapping will get access to it. All other code will solely rely on
 - * idmappings. This will get us type safety so it's impossible to conflate
 - * filesystems idmappings with mount idmappings.
 - *
 - * Return: The owner of the idmapped.
 - */
 -struct user_namespace *mnt_user_ns(const struct vfsmount *mnt)
 -{
 -      struct mnt_idmap *idmap = mnt_idmap(mnt);
 -
 -      /* Return the actual owner of the filesystem instead of the nop. */
 -      if (idmap == &nop_mnt_idmap &&
 -          !initial_idmapping(mnt->mnt_sb->s_user_ns))
 -              return mnt->mnt_sb->s_user_ns;
 -      return mnt_idmap_owner(idmap);
 -}
 -EXPORT_SYMBOL_GPL(mnt_user_ns);
 -
 -/**
 - * alloc_mnt_idmap - allocate a new idmapping for the mount
 - * @mnt_userns: owning userns of the idmapping
 - *
 - * Allocate a new struct mnt_idmap which carries the idmapping of the mount.
 - *
 - * Return: On success a new idmap, on error an error pointer is returned.
 - */
 -static struct mnt_idmap *alloc_mnt_idmap(struct user_namespace *mnt_userns)
 -{
 -      struct mnt_idmap *idmap;
 -
 -      idmap = kzalloc(sizeof(struct mnt_idmap), GFP_KERNEL_ACCOUNT);
 -      if (!idmap)
 -              return ERR_PTR(-ENOMEM);
 -
 -      idmap->owner = get_user_ns(mnt_userns);
 -      refcount_set(&idmap->count, 1);
 -      return idmap;
 -}
 -
 -/**
 - * mnt_idmap_get - get a reference to an idmapping
 - * @idmap: the idmap to bump the reference on
 - *
 - * If @idmap is not the @nop_mnt_idmap bump the reference count.
 - *
 - * Return: @idmap with reference count bumped if @not_mnt_idmap isn't passed.
 - */
 -static inline struct mnt_idmap *mnt_idmap_get(struct mnt_idmap *idmap)
 -{
 -      if (idmap != &nop_mnt_idmap)
 -              refcount_inc(&idmap->count);
 -
 -      return idmap;
 -}
 -
 -/**
 - * mnt_idmap_put - put a reference to an idmapping
 - * @idmap: the idmap to put the reference on
 - *
 - * If this is a non-initial idmapping, put the reference count when a mount is
 - * released and free it if we're the last user.
 - */
 -static inline void mnt_idmap_put(struct mnt_idmap *idmap)
 -{
 -      if (idmap != &nop_mnt_idmap && refcount_dec_and_test(&idmap->count)) {
 -              put_user_ns(idmap->owner);
 -              kfree(idmap);
 -      }
 -}
 -
  static struct mount *alloc_vfsmnt(const char *name)
  {
        struct mount *mnt = kmem_cache_zalloc(mnt_cache, GFP_KERNEL);
@@@ -1283,6 -1397,17 +1283,17 @@@ struct vfsmount *mntget(struct vfsmoun
  }
  EXPORT_SYMBOL(mntget);
  
+ /*
+  * Make a mount point inaccessible to new lookups.
+  * Because there may still be current users, the caller MUST WAIT
+  * for an RCU grace period before destroying the mount point.
+  */
+ void mnt_make_shortterm(struct vfsmount *mnt)
+ {
+       if (mnt)
+               real_mount(mnt)->mnt_ns = NULL;
+ }
  /**
   * path_is_mountpoint() - Check if path is a mount in the current namespace.
   * @path: path to check
@@@ -3980,7 -4105,7 +3991,7 @@@ static int can_idmap_mount(const struc
         * Creating an idmapped mount with the filesystem wide idmapping
         * doesn't make sense so block that. We don't allow mushy semantics.
         */
 -      if (mnt_idmap_owner(kattr->mnt_idmap) == fs_userns)
 +      if (!check_fsmapping(kattr->mnt_idmap, m->mnt_sb))
                return -EINVAL;
  
        /*
@@@ -4226,7 -4351,7 +4237,7 @@@ static int build_mount_idmapped(const s
         * result.
         */
        mnt_userns = container_of(ns, struct user_namespace, ns);
 -      if (initial_idmapping(mnt_userns)) {
 +      if (mnt_userns == &init_user_ns) {
                err = -EPERM;
                goto out_fput;
        }
@@@ -4459,8 -4584,8 +4470,8 @@@ EXPORT_SYMBOL_GPL(kern_mount)
  void kern_unmount(struct vfsmount *mnt)
  {
        /* release long term mount so mount point can be released */
-       if (!IS_ERR_OR_NULL(mnt)) {
-               real_mount(mnt)->mnt_ns = NULL;
+       if (!IS_ERR(mnt)) {
+               mnt_make_shortterm(mnt);
                synchronize_rcu();      /* yecchhh... */
                mntput(mnt);
        }
@@@ -4472,8 -4597,7 +4483,7 @@@ void kern_unmount_array(struct vfsmoun
        unsigned int i;
  
        for (i = 0; i < num; i++)
-               if (mnt[i])
-                       real_mount(mnt[i])->mnt_ns = NULL;
+               mnt_make_shortterm(mnt[i]);
        synchronize_rcu_expedited();
        for (i = 0; i < num; i++)
                mntput(mnt[i]);
diff --combined include/linux/mount.h
index 52f452b2259adb8ab1a1a23176c97a131f57de2e,ec55a031aa8c1db73ffd0c1d1c4dfb0a4756412f..1ea326c368f7261f952bcefc9d5735127ce1e8e0
@@@ -74,6 -74,8 +74,6 @@@ struct vfsmount 
        struct mnt_idmap *mnt_idmap;
  } __randomize_layout;
  
 -struct user_namespace *mnt_user_ns(const struct vfsmount *mnt);
 -struct user_namespace *mnt_idmap_owner(const struct mnt_idmap *idmap);
  static inline struct mnt_idmap *mnt_idmap(const struct vfsmount *mnt)
  {
        /* Pairs with smp_store_release() in do_idmap_mount(). */
@@@ -86,6 -88,7 +86,7 @@@ extern void mnt_drop_write(struct vfsmo
  extern void mnt_drop_write_file(struct file *file);
  extern void mntput(struct vfsmount *mnt);
  extern struct vfsmount *mntget(struct vfsmount *mnt);
+ extern void mnt_make_shortterm(struct vfsmount *mnt);
  extern struct vfsmount *mnt_clone_internal(const struct path *path);
  extern bool __mnt_is_readonly(struct vfsmount *mnt);
  extern bool mnt_may_suid(struct vfsmount *mnt);
diff --combined ipc/mqueue.c
index 0160e9f2b07c77cef7f1f3b9c1b28141407cad43,6c5bf7cce7fe3c559caf595ce7b1dfc0e4eb81be..71881bddad2577043f285fcf6173cd238c2d5df8
@@@ -608,7 -608,7 +608,7 @@@ out_unlock
        return error;
  }
  
 -static int mqueue_create(struct user_namespace *mnt_userns, struct inode *dir,
 +static int mqueue_create(struct mnt_idmap *idmap, struct inode *dir,
                         struct dentry *dentry, umode_t mode, bool excl)
  {
        return mqueue_create_attr(dentry, mode, NULL);
@@@ -887,7 -887,7 +887,7 @@@ static int prepare_open(struct dentry *
        if ((oflag & O_ACCMODE) == (O_RDWR | O_WRONLY))
                return -EINVAL;
        acc = oflag2acc[oflag & O_ACCMODE];
 -      return inode_permission(&init_user_ns, d_inode(dentry), acc);
 +      return inode_permission(&nop_mnt_idmap, d_inode(dentry), acc);
  }
  
  static int do_mq_open(const char __user *u_name, int oflag, umode_t mode,
@@@ -979,7 -979,7 +979,7 @@@ SYSCALL_DEFINE1(mq_unlink, const char _
                err = -ENOENT;
        } else {
                ihold(inode);
 -              err = vfs_unlink(&init_user_ns, d_inode(dentry->d_parent),
 +              err = vfs_unlink(&nop_mnt_idmap, d_inode(dentry->d_parent),
                                 dentry, NULL);
        }
        dput(dentry);
@@@ -1709,11 -1709,6 +1709,6 @@@ void mq_clear_sbinfo(struct ipc_namespa
        ns->mq_mnt->mnt_sb->s_fs_info = NULL;
  }
  
- void mq_put_mnt(struct ipc_namespace *ns)
- {
-       kern_unmount(ns->mq_mnt);
- }
  static int __init init_mqueue_fs(void)
  {
        int error;