kern_mount_data is a relatively expensive operation when creating a
new IPC namespace, so delay the mount until its first usage when not
creating the the global namespace.

This is a net saving for new IPC namespaces that don't use mq_open().
In this case there won't be any kern_mount_data() cost at all.

On my machine, the time for creating 1000 new IPC namespaces dropped
from ~8s to ~2s.

Signed-off-by: Giuseppe Scrivano <gscri...@redhat.com>
---

v1 here: https://lkml.org/lkml/2017/11/27/427

v1 -> v2:

Declare and use a mutex instead of a spinlock.

 include/linux/ipc_namespace.h |  2 +-
 ipc/mqueue.c                  | 48 ++++++++++++++++++++++++++++++++++---------
 ipc/namespace.c               |  2 +-
 3 files changed, 40 insertions(+), 12 deletions(-)

diff --git a/include/linux/ipc_namespace.h b/include/linux/ipc_namespace.h
index b5630c8eb2f3..a06617c113f1 100644
--- a/include/linux/ipc_namespace.h
+++ b/include/linux/ipc_namespace.h
@@ -81,7 +81,7 @@ static inline void shm_destroy_orphaned(struct ipc_namespace 
*ns) {}
 #endif /* CONFIG_SYSVIPC */
 
 #ifdef CONFIG_POSIX_MQUEUE
-extern int mq_init_ns(struct ipc_namespace *ns);
+extern int mq_init_ns(struct ipc_namespace *ns, bool mount);
 /*
  * POSIX Message Queue default values:
  *
diff --git a/ipc/mqueue.c b/ipc/mqueue.c
index d24025626310..27a82a3fef50 100644
--- a/ipc/mqueue.c
+++ b/ipc/mqueue.c
@@ -87,6 +87,7 @@ struct mqueue_inode_info {
        unsigned long qsize; /* size of queue in memory (sum of all msgs) */
 };
 
+static struct file_system_type mqueue_fs_type;
 static const struct inode_operations mqueue_dir_inode_operations;
 static const struct file_operations mqueue_file_operations;
 static const struct super_operations mqueue_super_ops;
@@ -776,9 +777,24 @@ static int do_mq_open(const char __user *u_name, int 
oflag, umode_t mode,
        struct filename *name;
        int fd, error;
        struct ipc_namespace *ipc_ns = current->nsproxy->ipc_ns;
-       struct vfsmount *mnt = ipc_ns->mq_mnt;
-       struct dentry *root = mnt->mnt_root;
+       struct vfsmount *mnt;
+       struct dentry *root;
        int ro;
+       static DEFINE_MUTEX(mnt_lock);
+
+       mutex_lock(&mnt_lock);
+       mnt = ipc_ns->mq_mnt;
+       if (unlikely(!mnt)) {
+               mnt = kern_mount_data(&mqueue_fs_type, ipc_ns);
+               if (IS_ERR(mnt)) {
+                       mutex_unlock(&mnt_lock);
+                       return PTR_ERR(mnt);
+               }
+               ipc_ns->mq_mnt = mnt;
+       }
+       mutex_unlock(&mnt_lock);
+
+       root = mnt->mnt_root;
 
        audit_mq_open(oflag, mode, attr);
 
@@ -863,6 +879,9 @@ SYSCALL_DEFINE1(mq_unlink, const char __user *, u_name)
        struct ipc_namespace *ipc_ns = current->nsproxy->ipc_ns;
        struct vfsmount *mnt = ipc_ns->mq_mnt;
 
+       if (!mnt)
+               return -ENOENT;
+
        name = getname(u_name);
        if (IS_ERR(name))
                return PTR_ERR(name);
@@ -1581,7 +1600,8 @@ static struct file_system_type mqueue_fs_type = {
        .fs_flags = FS_USERNS_MOUNT,
 };
 
-int mq_init_ns(struct ipc_namespace *ns)
+
+int mq_init_ns(struct ipc_namespace *ns, bool mount)
 {
        ns->mq_queues_count  = 0;
        ns->mq_queues_max    = DFLT_QUEUESMAX;
@@ -1590,23 +1610,31 @@ int mq_init_ns(struct ipc_namespace *ns)
        ns->mq_msg_default   = DFLT_MSG;
        ns->mq_msgsize_default  = DFLT_MSGSIZE;
 
-       ns->mq_mnt = kern_mount_data(&mqueue_fs_type, ns);
-       if (IS_ERR(ns->mq_mnt)) {
-               int err = PTR_ERR(ns->mq_mnt);
+       if (!mount)
                ns->mq_mnt = NULL;
-               return err;
+       else {
+               int err;
+
+               ns->mq_mnt = kern_mount_data(&mqueue_fs_type, ns);
+               if (IS_ERR(ns->mq_mnt)) {
+                       err = PTR_ERR(ns->mq_mnt);
+                       ns->mq_mnt = NULL;
+                       return err;
+               }
        }
        return 0;
 }
 
 void mq_clear_sbinfo(struct ipc_namespace *ns)
 {
-       ns->mq_mnt->mnt_sb->s_fs_info = NULL;
+       if (ns->mq_mnt)
+               ns->mq_mnt->mnt_sb->s_fs_info = NULL;
 }
 
 void mq_put_mnt(struct ipc_namespace *ns)
 {
-       kern_unmount(ns->mq_mnt);
+       if (ns->mq_mnt)
+               kern_unmount(ns->mq_mnt);
 }
 
 static int __init init_mqueue_fs(void)
@@ -1628,7 +1656,7 @@ static int __init init_mqueue_fs(void)
 
        spin_lock_init(&mq_lock);
 
-       error = mq_init_ns(&init_ipc_ns);
+       error = mq_init_ns(&init_ipc_ns, true);
        if (error)
                goto out_filesystem;
 
diff --git a/ipc/namespace.c b/ipc/namespace.c
index f59a89966f92..9d3689577f66 100644
--- a/ipc/namespace.c
+++ b/ipc/namespace.c
@@ -65,7 +65,7 @@ static struct ipc_namespace *create_ipc_ns(struct 
user_namespace *user_ns,
        if (err)
                goto fail_destroy_msg;
 
-       err = mq_init_ns(ns);
+       err = mq_init_ns(ns, false);
        if (err)
                goto fail_destroy_shm;
 
-- 
2.14.3

Reply via email to