diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2017-12-25 19:43:35 -0500 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2018-01-05 11:54:37 -0500 |
commit | 36735a6a2b5e042db1af956ce4bcc13f3ff99e21 (patch) | |
tree | ce8d6eddf34be533c2c12d6f68f916b9fb3dd035 /ipc | |
parent | a713fd7f529e361d42aa9dcc511e3fd3016599fe (diff) |
mqueue: switch to on-demand creation of internal mount
Instead of doing that upon each ipcns creation, we do that the first
time mq_open(2) or mqueue mount is done in an ipcns. What's more,
doing that allows to get rid of mount_ns() use - we can go with
considerably cheaper mount_nodev(), avoiding the loop over all
mqueue superblock instances; ipcns->mq_mnt is used to locate preexisting
instance in O(1) time instead of O(instances) mount_ns() would've
cost us.
Based upon the version by Giuseppe Scrivano <gscrivan@redhat.com>; I've
added handling of userland mqueue mounts (original had been broken in
that area) and added a switch to mount_nodev().
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'ipc')
-rw-r--r-- | ipc/mqueue.c | 74 |
1 files changed, 55 insertions, 19 deletions
diff --git a/ipc/mqueue.c b/ipc/mqueue.c index 9f05837a86a6..738579191fd2 100644 --- a/ipc/mqueue.c +++ b/ipc/mqueue.c | |||
@@ -325,8 +325,9 @@ err: | |||
325 | static int mqueue_fill_super(struct super_block *sb, void *data, int silent) | 325 | static int mqueue_fill_super(struct super_block *sb, void *data, int silent) |
326 | { | 326 | { |
327 | struct inode *inode; | 327 | struct inode *inode; |
328 | struct ipc_namespace *ns = sb->s_fs_info; | 328 | struct ipc_namespace *ns = data; |
329 | 329 | ||
330 | sb->s_fs_info = ns; | ||
330 | sb->s_iflags |= SB_I_NOEXEC | SB_I_NODEV; | 331 | sb->s_iflags |= SB_I_NOEXEC | SB_I_NODEV; |
331 | sb->s_blocksize = PAGE_SIZE; | 332 | sb->s_blocksize = PAGE_SIZE; |
332 | sb->s_blocksize_bits = PAGE_SHIFT; | 333 | sb->s_blocksize_bits = PAGE_SHIFT; |
@@ -343,18 +344,44 @@ static int mqueue_fill_super(struct super_block *sb, void *data, int silent) | |||
343 | return 0; | 344 | return 0; |
344 | } | 345 | } |
345 | 346 | ||
347 | static struct file_system_type mqueue_fs_type; | ||
348 | /* | ||
349 | * Return value is pinned only by reference in ->mq_mnt; it will | ||
350 | * live until ipcns dies. Caller does not need to drop it. | ||
351 | */ | ||
352 | static struct vfsmount *mq_internal_mount(void) | ||
353 | { | ||
354 | struct ipc_namespace *ns = current->nsproxy->ipc_ns; | ||
355 | struct vfsmount *m = ns->mq_mnt; | ||
356 | if (m) | ||
357 | return m; | ||
358 | m = kern_mount_data(&mqueue_fs_type, ns); | ||
359 | spin_lock(&mq_lock); | ||
360 | if (unlikely(ns->mq_mnt)) { | ||
361 | spin_unlock(&mq_lock); | ||
362 | if (!IS_ERR(m)) | ||
363 | kern_unmount(m); | ||
364 | return ns->mq_mnt; | ||
365 | } | ||
366 | if (!IS_ERR(m)) | ||
367 | ns->mq_mnt = m; | ||
368 | spin_unlock(&mq_lock); | ||
369 | return m; | ||
370 | } | ||
371 | |||
346 | static struct dentry *mqueue_mount(struct file_system_type *fs_type, | 372 | static struct dentry *mqueue_mount(struct file_system_type *fs_type, |
347 | int flags, const char *dev_name, | 373 | int flags, const char *dev_name, |
348 | void *data) | 374 | void *data) |
349 | { | 375 | { |
350 | struct ipc_namespace *ns; | 376 | struct vfsmount *m; |
351 | if (flags & SB_KERNMOUNT) { | 377 | if (flags & SB_KERNMOUNT) |
352 | ns = data; | 378 | return mount_nodev(fs_type, flags, data, mqueue_fill_super); |
353 | data = NULL; | 379 | m = mq_internal_mount(); |
354 | } else { | 380 | if (IS_ERR(m)) |
355 | ns = current->nsproxy->ipc_ns; | 381 | return ERR_CAST(m); |
356 | } | 382 | atomic_inc(&m->mnt_sb->s_active); |
357 | return mount_ns(fs_type, flags, data, ns, ns->user_ns, mqueue_fill_super); | 383 | down_write(&m->mnt_sb->s_umount); |
384 | return dget(m->mnt_root); | ||
358 | } | 385 | } |
359 | 386 | ||
360 | static void init_once(void *foo) | 387 | static void init_once(void *foo) |
@@ -743,13 +770,16 @@ static int prepare_open(struct dentry *dentry, int oflag, int ro, | |||
743 | static int do_mq_open(const char __user *u_name, int oflag, umode_t mode, | 770 | static int do_mq_open(const char __user *u_name, int oflag, umode_t mode, |
744 | struct mq_attr *attr) | 771 | struct mq_attr *attr) |
745 | { | 772 | { |
746 | struct vfsmount *mnt = current->nsproxy->ipc_ns->mq_mnt; | 773 | struct vfsmount *mnt = mq_internal_mount(); |
747 | struct dentry *root = mnt->mnt_root; | 774 | struct dentry *root; |
748 | struct filename *name; | 775 | struct filename *name; |
749 | struct path path; | 776 | struct path path; |
750 | int fd, error; | 777 | int fd, error; |
751 | int ro; | 778 | int ro; |
752 | 779 | ||
780 | if (IS_ERR(mnt)) | ||
781 | return PTR_ERR(mnt); | ||
782 | |||
753 | audit_mq_open(oflag, mode, attr); | 783 | audit_mq_open(oflag, mode, attr); |
754 | 784 | ||
755 | if (IS_ERR(name = getname(u_name))) | 785 | if (IS_ERR(name = getname(u_name))) |
@@ -760,6 +790,7 @@ static int do_mq_open(const char __user *u_name, int oflag, umode_t mode, | |||
760 | goto out_putname; | 790 | goto out_putname; |
761 | 791 | ||
762 | ro = mnt_want_write(mnt); /* we'll drop it in any case */ | 792 | ro = mnt_want_write(mnt); /* we'll drop it in any case */ |
793 | root = mnt->mnt_root; | ||
763 | inode_lock(d_inode(root)); | 794 | inode_lock(d_inode(root)); |
764 | path.dentry = lookup_one_len(name->name, root, strlen(name->name)); | 795 | path.dentry = lookup_one_len(name->name, root, strlen(name->name)); |
765 | if (IS_ERR(path.dentry)) { | 796 | if (IS_ERR(path.dentry)) { |
@@ -808,6 +839,9 @@ SYSCALL_DEFINE1(mq_unlink, const char __user *, u_name) | |||
808 | struct ipc_namespace *ipc_ns = current->nsproxy->ipc_ns; | 839 | struct ipc_namespace *ipc_ns = current->nsproxy->ipc_ns; |
809 | struct vfsmount *mnt = ipc_ns->mq_mnt; | 840 | struct vfsmount *mnt = ipc_ns->mq_mnt; |
810 | 841 | ||
842 | if (!mnt) | ||
843 | return -ENOENT; | ||
844 | |||
811 | name = getname(u_name); | 845 | name = getname(u_name); |
812 | if (IS_ERR(name)) | 846 | if (IS_ERR(name)) |
813 | return PTR_ERR(name); | 847 | return PTR_ERR(name); |
@@ -1534,28 +1568,26 @@ int mq_init_ns(struct ipc_namespace *ns) | |||
1534 | ns->mq_msgsize_max = DFLT_MSGSIZEMAX; | 1568 | ns->mq_msgsize_max = DFLT_MSGSIZEMAX; |
1535 | ns->mq_msg_default = DFLT_MSG; | 1569 | ns->mq_msg_default = DFLT_MSG; |
1536 | ns->mq_msgsize_default = DFLT_MSGSIZE; | 1570 | ns->mq_msgsize_default = DFLT_MSGSIZE; |
1571 | ns->mq_mnt = NULL; | ||
1537 | 1572 | ||
1538 | ns->mq_mnt = kern_mount_data(&mqueue_fs_type, ns); | ||
1539 | if (IS_ERR(ns->mq_mnt)) { | ||
1540 | int err = PTR_ERR(ns->mq_mnt); | ||
1541 | ns->mq_mnt = NULL; | ||
1542 | return err; | ||
1543 | } | ||
1544 | return 0; | 1573 | return 0; |
1545 | } | 1574 | } |
1546 | 1575 | ||
1547 | void mq_clear_sbinfo(struct ipc_namespace *ns) | 1576 | void mq_clear_sbinfo(struct ipc_namespace *ns) |
1548 | { | 1577 | { |
1549 | ns->mq_mnt->mnt_sb->s_fs_info = NULL; | 1578 | if (ns->mq_mnt) |
1579 | ns->mq_mnt->mnt_sb->s_fs_info = NULL; | ||
1550 | } | 1580 | } |
1551 | 1581 | ||
1552 | void mq_put_mnt(struct ipc_namespace *ns) | 1582 | void mq_put_mnt(struct ipc_namespace *ns) |
1553 | { | 1583 | { |
1554 | kern_unmount(ns->mq_mnt); | 1584 | if (ns->mq_mnt) |
1585 | kern_unmount(ns->mq_mnt); | ||
1555 | } | 1586 | } |
1556 | 1587 | ||
1557 | static int __init init_mqueue_fs(void) | 1588 | static int __init init_mqueue_fs(void) |
1558 | { | 1589 | { |
1590 | struct vfsmount *m; | ||
1559 | int error; | 1591 | int error; |
1560 | 1592 | ||
1561 | mqueue_inode_cachep = kmem_cache_create("mqueue_inode_cache", | 1593 | mqueue_inode_cachep = kmem_cache_create("mqueue_inode_cache", |
@@ -1577,6 +1609,10 @@ static int __init init_mqueue_fs(void) | |||
1577 | if (error) | 1609 | if (error) |
1578 | goto out_filesystem; | 1610 | goto out_filesystem; |
1579 | 1611 | ||
1612 | m = kern_mount_data(&mqueue_fs_type, &init_ipc_ns); | ||
1613 | if (IS_ERR(m)) | ||
1614 | goto out_filesystem; | ||
1615 | init_ipc_ns.mq_mnt = m; | ||
1580 | return 0; | 1616 | return 0; |
1581 | 1617 | ||
1582 | out_filesystem: | 1618 | out_filesystem: |