Re: [PATCH RESEND v3 1/4] IPC: Added two new system call mq_recvmmsg() and mq_sendmmsg()
Andrei Vagin <[email protected]> Sat, 20 Jun 2026 22:29:22 -0700
| Newsgroups | dev.linux.lists.criu |
|---|---|
| Message-ID | <CANaxB-zVrQ5zhGHFU7_J_hBKApE8Vg8eXhtzMnLncBarV92AxQ@mail.gmail.com> |
On Sat, Jun 20, 2026 at 4:09=E2=80=AFPM Mathura_Kumar <[email protected]> wrote: > > Implement two new POSIX message queue system calls, mq_sendmmsg() and > mq_recvmmsg(), analogous to the existing sendmmsg()/recvmmsg() socket > system calls.These allow sending and receiving or peek multiple messages = in a > single syscall, reducing the overhead of repeated context switches per di= screte > message. > > It contains the core implementation of both system calls as > part of a larger patchset. > > int mq_sendmmsg(mqd_t mqdes, struct mq_mmsg_attrs *attrs, > unsigned int attrs_len, unsigned long start_idx, > const struct __kernel_timespec *u_abs_timeout); > > int mq_recvmmsg(mqd_t mqdes, struct mq_mmsg_attrs *attrs, > unsigned int attrs_len, unsigned int flags, > unsigned long start_idx, const struct __kernel_timespec = *u_abs_timeout); > > Implementation complete details available under > mq_recvmmsg.rst and mq_sendmmsg.rst Please don't forget to run ./scripts/checkpatch.pl. It reports many warning= s for this patch. I think tabstop is set incorrectly in your editor. Here are a few links that you may consider to read: https://kernelnewbies.org/FirstKernelPatch https://docs.kernel.org/process/1.Intro.html > > Signed-off-by: Mathura_Kumar <[email protected]> > --- > include/linux/compat.h | 12 +- > include/linux/syscalls.h | 10 + > include/uapi/asm-generic/unistd.h | 9 +- > include/uapi/linux/mqueue.h | 27 +- > ipc/mqueue.c | 564 ++++++++++++++++++++++++++++-- > ipc/msg.c | 2 +- > ipc/msgutil.c | 48 ++- > ipc/util.h | 3 +- > kernel/sys_ni.c | 6 + > 9 files changed, 616 insertions(+), 65 deletions(-) > > diff --git a/include/linux/compat.h b/include/linux/compat.h > index 8da0a15c95f4..87ba67ae700d 100644 > --- a/include/linux/compat.h > +++ b/include/linux/compat.h > @@ -18,10 +18,10 @@ > #include <linux/aio_abi.h> /* for aio_context_t */ > #include <linux/uaccess.h> > #include <linux/unistd.h> > - > #include <asm/compat.h> > #include <asm/siginfo.h> > #include <asm/signal.h> > +#include <linux/mqueue.h> > > #ifdef CONFIG_ARCH_HAS_SYSCALL_WRAPPER > /* > @@ -428,6 +428,8 @@ struct compat_sysctl_args; > struct compat_kexec_segment; > struct compat_mq_attr; > struct compat_msgbuf; > +struct compat_msg_attrs; > +struct compat_mq_mmsg_attrs; > > void copy_siginfo_to_external32(struct compat_siginfo *to, > const struct kernel_siginfo *from); > @@ -805,8 +807,12 @@ asmlinkage long compat_sys_pwritev64v2(unsigned long= fd, > const struct iovec __user *vec, > unsigned long vlen, loff_t pos, rwf_t flags); > #endif > - > - > +asmlinkage long compat_sys_mq_sendmmsg(mqd_t mqdes, struct compat_mq_mms= g_attrs __user *attrs, > + = unsigned int attrs_len, unsigned long start_index, > + = const struct __kernel_timespec __user *abs_timeout); > +asmlinkage long compat_sys_mq_recvmmsg(mqd_t mqdes, struct compat_mq_mms= g_attrs __user *attrs, > + = unsigned int attrs_len, unsigned int flags, unsigned long start_index, > + = const struct __kernel_timespec __user *abs_timeout); > /* > * Deprecated system calls which are still defined in > * include/uapi/asm-generic/unistd.h and wanted by >=3D 1 arch > diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h > index 4fb7291f54b6..98979e26de55 100644 > --- a/include/linux/syscalls.h > +++ b/include/linux/syscalls.h > @@ -9,6 +9,7 @@ > #ifndef _LINUX_SYSCALLS_H > #define _LINUX_SYSCALLS_H > > +#include "linux/mqueue.h" > struct __aio_sigset; > struct epoll_event; > struct iattr; > @@ -79,6 +80,8 @@ struct mnt_id_req; > struct ns_id_req; > struct xattr_args; > struct file_attr; > +struct mq_msg_attrs; > +struct mq_mmsg_attrs; > > #include <linux/types.h> > #include <linux/aio_abi.h> > @@ -93,6 +96,7 @@ struct file_attr; > #include <linux/key.h> > #include <linux/personality.h> > #include <trace/syscall.h> > +#include <linux/mqueue.h> > > #ifdef CONFIG_ARCH_HAS_SYSCALL_WRAPPER > /* > @@ -739,7 +743,13 @@ asmlinkage long sys_sysinfo(struct sysinfo __user *i= nfo); > asmlinkage long sys_mq_open(const char __user *name, int oflag, umode_t = mode, struct mq_attr __user *attr); > asmlinkage long sys_mq_unlink(const char __user *name); > asmlinkage long sys_mq_timedsend(mqd_t mqdes, const char __user *msg_ptr= , size_t msg_len, unsigned int msg_prio, const struct __kernel_timespec __u= ser *abs_timeout); > +asmlinkage long sys_mq_sendmmsg(mqd_t mqdes, struct mq_mmsg_attrs __user= *attrs, > + unsigned int a= ttrs_len, unsigned long start_index, > + const struct _= _kernel_timespec __user *abs_timeout); > asmlinkage long sys_mq_timedreceive(mqd_t mqdes, char __user *msg_ptr, s= ize_t msg_len, unsigned int __user *msg_prio, const struct __kernel_timespe= c __user *abs_timeout); > +asmlinkage long sys_mq_recvmmsg(mqd_t mqdes, struct mq_mmsg_attrs __user= *attrs, > + unsigned int a= ttrs_len, unsigned int flags, unsigned long start_index, > + const struct _= _kernel_timespec __user *abs_timeout); > asmlinkage long sys_mq_notify(mqd_t mqdes, const struct sigevent __user = *notification); > asmlinkage long sys_mq_getsetattr(mqd_t mqdes, const struct mq_attr __us= er *mqstat, struct mq_attr __user *omqstat); > asmlinkage long sys_mq_timedreceive_time32(mqd_t mqdes, > diff --git a/include/uapi/asm-generic/unistd.h b/include/uapi/asm-generic= /unistd.h > index a627acc8fb5f..1d06486d3aa5 100644 > --- a/include/uapi/asm-generic/unistd.h > +++ b/include/uapi/asm-generic/unistd.h > @@ -863,9 +863,14 @@ __SYSCALL(__NR_listns, sys_listns) > #define __NR_rseq_slice_yield 471 > __SYSCALL(__NR_rseq_slice_yield, sys_rseq_slice_yield) > > -#undef __NR_syscalls > -#define __NR_syscalls 472 > +#define __NR_mq_recvmmsg 472 > +__SC_COMP(__NR_mq_recvmmsg, sys_mq_recvmmsg, compat_sys_mq_recvmmsg) > + > +#define __NR_mq_sendmmsg 473 > +__SC_COMP(__NR_mq_sendmmsg, sys_mq_sendmmsg, compat_sys_mq_sendmmsg) > > +#undef __NR_syscalls > +#define __NR_syscalls 474 > /* > * 32 bit systems traditionally used different > * syscalls for off_t and loff_t arguments, while > diff --git a/include/uapi/linux/mqueue.h b/include/uapi/linux/mqueue.h > index b516b66840ad..836bb77a6758 100644 > --- a/include/uapi/linux/mqueue.h > +++ b/include/uapi/linux/mqueue.h > @@ -18,8 +18,9 @@ > > #ifndef _LINUX_MQUEUE_H > #define _LINUX_MQUEUE_H > - > +#include <linux/uio.h> > #include <linux/types.h> > +#include <asm/compat.h> > > #define MQ_PRIO_MAX 32768 > /* per-uid limit of kernel memory used by mqueue, in bytes */ > @@ -33,6 +34,30 @@ struct mq_attr { > __kernel_long_t __reserved[4]; /* ignored for input, zeroed for = output */ > }; > > +struct mq_msg_attrs { > + __kernel_size_t msg_len; > + unsigned int __user *msg_prio; > + void __user *msg_ptr; > +}; > + > +struct mq_mmsg_attrs { > + struct iovec __user *msg_attrs_vec; > + __kernel_size_t vlen; > + int __user *ret; > +}; > + > +struct compat_msg_attrs { > + compat_size_t msg_len; > + compat_uptr_t __user msg_prio; > + compat_uptr_t __user msg_ptr; > +}; > + > +struct compat_mq_mmsg_attrs { > + compat_uptr_t __user msg_attrs_vec; > + compat_size_t vlen; > + compat_uptr_t __user ret; > +}; > + > /* > * SIGEV_THREAD implementation: > * SIGEV_THREAD must be implemented in user space. If SIGEV_THREAD is pa= ssed > diff --git a/ipc/mqueue.c b/ipc/mqueue.c > index 4798b375972b..c568c013a5ad 100644 > --- a/ipc/mqueue.c > +++ b/ipc/mqueue.c > @@ -38,8 +38,10 @@ > #include <linux/sched/wake_q.h> > #include <linux/sched/signal.h> > #include <linux/sched/user.h> > - > +#include <linux/uio.h> > +#include <linux/uaccess.h> > #include <net/sock.h> > +#include <linux/rbtree_augmented.h> > #include "util.h" > > struct mqueue_fs_context { > @@ -54,6 +56,10 @@ struct mqueue_fs_context { > #define SEND 0 > #define RECV 1 > > +#define MQ_PEEK 0x02 > +#define MQ_RECV 0x04 > +#define MQ_VALID_FLAGS (MQ_PEEK | MQ_RECV) > + > #define STATE_NONE 0 > #define STATE_READY 1 > > @@ -61,6 +67,8 @@ struct posix_msg_tree_node { > struct rb_node rb_node; > struct list_head msg_list; > int priority; > + unsigned int msg_count; /* Total messages at exactly this priorit= y */ > + unsigned int subtree_msg_count; /* sum of messages in this node a= nd all descendants */ > }; > > /* > @@ -186,7 +194,41 @@ static struct ipc_namespace *get_ns_from_inode(struc= t inode *inode) > return ns; > } > > -/* Auxiliary functions to manipulate messages' list */ > +static inline unsigned int get_subtree_count(struct rb_node *node) > +{ > + if (!node) > + return 0; > + return rb_entry(node, struct posix_msg_tree_node, rb_node)->subtr= ee_msg_count; > +} > + > +static void msg_tree_propagate_subtree_msg_count(struct rb_node *node, s= truct rb_node *stop) > +{ > + while (node !=3D stop) { > + struct posix_msg_tree_node *leaf =3D rb_entry(node, struc= t posix_msg_tree_node, rb_node); > + unsigned int new_count =3D leaf->msg_count + > + get_subtree_count(node->rb_left)= + > + get_subtree_count(node->rb_right= ); > + if (leaf->subtree_msg_count =3D=3D new_count) > + break; > + leaf->subtree_msg_count =3D new_count; > + node =3D rb_parent(node); > + } > +} > + > +static void msg_tree_copy_subtree_msg_count(struct rb_node *old, struct = rb_node *new) > +{ > + struct posix_msg_tree_node *old_leaf =3D rb_entry(old, struct pos= ix_msg_tree_node, rb_node); > + struct posix_msg_tree_node *new_leaf =3D rb_entry(new, struct pos= ix_msg_tree_node, rb_node); > + > + new_leaf->subtree_msg_count =3D old_leaf->subtree_msg_count; > +} > + > +static const struct rb_augment_callbacks msg_tree_callbacks =3D { > + .propagate =3D msg_tree_propagate_subtree_msg_count, > + .copy =3D msg_tree_copy_subtree_msg_count, > + .rotate =3D msg_tree_propagate_subtree_msg_count, > +}; > + > static int msg_insert(struct msg_msg *msg, struct mqueue_inode_info *inf= o) > { > struct rb_node **p, *parent =3D NULL; > @@ -216,13 +258,19 @@ static int msg_insert(struct msg_msg *msg, struct m= queue_inode_info *info) > INIT_LIST_HEAD(&leaf->msg_list); > } > leaf->priority =3D msg->m_type; > + leaf->msg_count =3D 1; > + leaf->subtree_msg_count =3D 1; > > if (rightmost) > info->msg_tree_rightmost =3D &leaf->rb_node; > > rb_link_node(&leaf->rb_node, parent, p); > - rb_insert_color(&leaf->rb_node, &info->msg_tree); > + rb_insert_augmented(&leaf->rb_node, &info->msg_tree, &msg_tree_ca= llbacks); > + goto common_insert; > insert_msg: > + leaf->msg_count++; > + msg_tree_propagate_subtree_msg_count(&leaf->rb_node, NULL); > +common_insert: > info->attr.mq_curmsgs++; > info->qsize +=3D msg->m_ts; > list_add_tail(&msg->m_list, &leaf->msg_list); > @@ -236,8 +284,7 @@ static inline void msg_tree_erase(struct posix_msg_tr= ee_node *leaf, > > if (info->msg_tree_rightmost =3D=3D node) > info->msg_tree_rightmost =3D rb_prev(node); > - > - rb_erase(node, &info->msg_tree); > + rb_erase_augmented(node, &info->msg_tree, &msg_tree_callbacks); > if (info->node_cache) > kfree(leaf); > else > @@ -277,8 +324,11 @@ static inline struct msg_msg *msg_get(struct mqueue_= inode_info *info) > msg =3D list_first_entry(&leaf->msg_list, > struct msg_msg, m_list); > list_del(&msg->m_list); > + leaf->msg_count--; > if (list_empty(&leaf->msg_list)) { > msg_tree_erase(leaf, info); > + } else { > + msg_tree_propagate_subtree_msg_count(&leaf->rb_no= de, NULL); > } > } > info->attr.mq_curmsgs--; > @@ -765,7 +815,6 @@ static struct ext_wait_queue *wq_get_first_waiter( > return list_entry(ptr, struct ext_wait_queue, list); > } > > - > static inline void set_cookie(struct sk_buff *skb, char code) > { > ((char *)skb->data)[NOTIFY_COOKIE_LEN-1] =3D code; > @@ -1034,28 +1083,22 @@ static inline void pipelined_receive(struct wake_= q_head *wake_q, > __pipelined_op(wake_q, info, sender); > } > > -static int do_mq_timedsend(mqd_t mqdes, const char __user *u_msg_ptr, > - size_t msg_len, unsigned int msg_prio, > - struct timespec64 *ts) > +static ssize_t do_mq_sendmsg(mqd_t mqdes, const char __user *u_msg_ptr, > + size_t msg_len, unsigned = int msg_prio, > + const struct timespec64 *= ts, ktime_t *timeout) > { > struct inode *inode; > struct ext_wait_queue wait; > struct ext_wait_queue *receiver; > struct msg_msg *msg_ptr; > struct mqueue_inode_info *info; > - ktime_t expires, *timeout =3D NULL; > struct posix_msg_tree_node *new_leaf =3D NULL; > - int ret =3D 0; > + ssize_t ret =3D 0; > DEFINE_WAKE_Q(wake_q); > > if (unlikely(msg_prio >=3D (unsigned long) MQ_PRIO_MAX)) > return -EINVAL; > > - if (ts) { > - expires =3D timespec64_to_ktime(*ts); > - timeout =3D &expires; > - } > - > audit_mq_sendrecv(mqdes, msg_len, msg_prio, ts); > > CLASS(fd, f)(mqdes); > @@ -1139,23 +1182,31 @@ static int do_mq_timedsend(mqd_t mqdes, const cha= r __user *u_msg_ptr, > return ret; > } > > -static int do_mq_timedreceive(mqd_t mqdes, char __user *u_msg_ptr, > - size_t msg_len, unsigned int __user *u_msg_prio, > - struct timespec64 *ts) > +static ssize_t do_mq_timedsend(mqd_t mqdes, const char __user *u_msg_ptr= , > + size_t msg_len, unsigne= d int msg_prio, > + struct timespec64 *ts) > { > - ssize_t ret; > - struct msg_msg *msg_ptr; > - struct inode *inode; > - struct mqueue_inode_info *info; > - struct ext_wait_queue wait; > ktime_t expires, *timeout =3D NULL; > - struct posix_msg_tree_node *new_leaf =3D NULL; > > if (ts) { > expires =3D timespec64_to_ktime(*ts); > timeout =3D &expires; > } > > + return do_mq_sendmsg(mqdes, u_msg_ptr, msg_len, msg_prio, ts, tim= eout); > +} > + > +static ssize_t do_mq_recvmsg(mqd_t mqdes, char __user *u_msg_ptr, size_t= msg_len, > + unsigned int __user *u_ms= g_prio, const struct timespec64 *ts, > + ktime_t *timeout) > +{ > + ssize_t ret; > + struct msg_msg *msg_ptr; > + struct inode *inode; > + struct mqueue_inode_info *info; > + struct ext_wait_queue wait; > + struct posix_msg_tree_node *new_leaf =3D NULL; > + > audit_mq_sendrecv(mqdes, msg_len, 0, ts); > > CLASS(fd, f)(mqdes); > @@ -1230,13 +1281,364 @@ static int do_mq_timedreceive(mqd_t mqdes, char = __user *u_msg_ptr, > return ret; > } > > +static ssize_t do_mq_timedreceive(mqd_t mqdes, char __user *u_msg_ptr, s= ize_t msg_len, > + unsigned int __u= ser *u_msg_prio, struct timespec64 *ts) > +{ > + ktime_t expires, *timeout =3D NULL; > + > + if (ts) { > + expires =3D timespec64_to_ktime(*ts); > + timeout =3D &expires; > + } > + > + return do_mq_recvmsg(mqdes, u_msg_ptr, msg_len, u_msg_prio, ts, t= imeout); > +} > + > +static struct msg_msg *mq_peek_index(struct mqueue_inode_info *info, uns= igned long index) > +{ > + struct rb_node *node; > + struct posix_msg_tree_node *leaf; > + struct msg_msg *msg; > + unsigned int right_count; > + unsigned int offset; > + unsigned int i =3D 0; > + > + node =3D info->msg_tree.rb_node; > + while (node) { > + leaf =3D rb_entry(node, struct posix_msg_tree_node, rb_no= de); > + right_count =3D get_subtree_count(node->rb_right); > + > + if (index < right_count) { > + node =3D node->rb_right; > + } else if (index < (right_count + leaf->msg_count)) { > + /* Target is at this priority level */ > + offset =3D index - right_count; > + list_for_each_entry(msg, &leaf->msg_list, m_list)= { > + if (i =3D=3D offset) > + return msg; > + i++; > + } > + break; > + } else { > + index -=3D (right_count + leaf->msg_count); > + node =3D node->rb_left; > + } > + } > + > + return NULL; > +} > + > +static ssize_t do_mq_recvmsg2(mqd_t mqdes, struct mq_msg_attrs *args, un= signed int flags, > + unsigned long index, con= st struct timespec64 *ts, > + ktime_t *timeout) > +{ > + ssize_t ret; > + struct msg_msg *msg_ptr, *k_msg_buffer; > + long k_m_type; > + size_t k_m_ts; > + struct inode *inode; > + struct mqueue_inode_info *info; > + > + if (flags & MQ_PEEK) { > + audit_mq_sendrecv(mqdes, args->msg_len, 0, ts); > + > + CLASS(fd, f)(mqdes); > + if (fd_empty(f)) > + return -EBADF; > + > + inode =3D file_inode(fd_file(f)); > + if (unlikely(fd_file(f)->f_op !=3D &mqueue_file_operation= s)) > + return -EBADF; > + > + info =3D MQUEUE_I(inode); > + audit_file(fd_file(f)); > + if (unlikely(!(fd_file(f)->f_mode & FMODE_READ))) > + return -EBADF; > + > + if (unlikely(args->msg_len < info->attr.mq_msgsize)) > + return -EMSGSIZE; > + > + if (index >=3D (unsigned long)info->attr.mq_maxmsg) > + return -EINVAL; > + > + spin_lock(&info->lock); > + > + if (info->attr.mq_curmsgs =3D=3D 0) { > + spin_unlock(&info->lock); > + return -EAGAIN; > + } > + msg_ptr =3D mq_peek_index(info, index); > + if (!msg_ptr) { > + spin_unlock(&info->lock); > + return -ENODATA; > + } > + k_m_type =3D msg_ptr->m_type; > + k_m_ts =3D msg_ptr->m_ts; > + > + spin_unlock(&info->lock); > + > + k_msg_buffer =3D alloc_msg(k_m_ts); > + > + if (!k_msg_buffer) > + return -ENOMEM; > + ret =3D security_msg_msg_alloc(k_msg_buffer); > + if (ret) { > + free_msg(k_msg_buffer); > + return ret; > + } > + > + /* > + * Two spin locks are necessary here. We are avoiding atomic memo= ry > + * allocation and premature allocation before confirming > + * a message actually exists to peek and retrieving required buff= er size > + * when first lock was taken. > + */ > + spin_lock(&info->lock); > + > + msg_ptr =3D mq_peek_index(info, index); > + if (!msg_ptr || msg_ptr->m_type !=3D k_m_type || > + msg_ptr->m_ts !=3D k_m_ts) { > + spin_unlock(&info->lock); > + free_msg(k_msg_buffer); > + return -EAGAIN; > + } > + msg_ptr =3D copy_msg(msg_ptr, k_msg_buffer, k_m_ts); > + if (IS_ERR(msg_ptr)) { > + spin_unlock(&info->lock); > + free_msg(k_msg_buffer); > + return PTR_ERR(msg_ptr); > + } > + spin_unlock(&info->lock); > + > + ret =3D k_msg_buffer->m_ts; > + if (args->msg_prio && put_user(k_m_type, args->msg_prio))= { > + free_msg(k_msg_buffer); > + return -EFAULT; > + } > + if (store_msg((char *)args->msg_ptr, k_msg_buffer, k_m_ts= )) { > + free_msg(k_msg_buffer); > + return -EFAULT; > + } > + free_msg(k_msg_buffer); > + return ret; > + } > + if (flags & MQ_RECV) { > + return do_mq_recvmsg(mqdes, (char *)args->msg_ptr, args->= msg_len, > + args->msg_prio, ts, timeout); > + } > + > + return -EINVAL; > +} > + > +static int mq_mmsg_copy_attrs_from_user(struct mq_mmsg_attrs *attrs, > + = const struct mq_mmsg_attrs __user *uattrs, > + = unsigned int attrs_len) > +{ > + if (unlikely(attrs_len < sizeof(*attrs))) > + return -EINVAL; > + if (unlikely(attrs_len > PAGE_SIZE)) > + return -E2BIG; > + return copy_struct_from_user(attrs, sizeof(*attrs), uattrs, attrs= _len); > +} > + > +#ifdef CONFIG_COMPAT > +static int mq_mmsg_copy_compat_attrs(struct mq_mmsg_attrs *attrs, > + c= onst struct compat_mq_mmsg_attrs __user *uattrs, > + u= nsigned int attrs_len) > +{ > + struct compat_mq_mmsg_attrs v =3D {}; > + int err; > + > + if (unlikely(attrs_len < sizeof(v))) > + return -EINVAL; > + if (unlikely(attrs_len > PAGE_SIZE)) > + return -E2BIG; > + err =3D copy_struct_from_user(&v, sizeof(v), uattrs, attrs_len); > + > + if (err) > + return err; > + attrs->msg_attrs_vec =3D (struct iovec __user *)compat_ptr((unsig= ned long)v.msg_attrs_vec); > + attrs->ret =3D (int *)compat_ptr(v.ret); > + attrs->vlen =3D v.vlen; > + return 0; > +} > + > +static int mq_mmsg_copy_compat_msg_attr(struct mq_msg_attrs *attr, > + = const struct iovec *desc_iov) > +{ > + struct compat_msg_attrs v =3D {}; > + > + if (desc_iov->iov_len !=3D sizeof(v)) > + return -EINVAL; > + if (copy_from_user(&v, desc_iov->iov_base, sizeof(v))) > + return -EFAULT; > + > + attr->msg_len =3D v.msg_len; > + attr->msg_prio =3D (unsigned int *)compat_ptr(v.msg_prio)= ; > + attr->msg_ptr =3D compat_ptr(v.msg_ptr); > + return 0; > + } > + > +#endif > + > +static int mq_mmsg_copy_msg_attr(struct mq_msg_attrs *attr, > + const str= uct iovec *desc_iov, bool compat) > +{ > + if (compat) > + return mq_mmsg_copy_compat_msg_attr(attr, desc_iov); > + if (desc_iov->iov_len !=3D sizeof(*attr)) > + return -EINVAL; > + if (copy_from_user(attr, desc_iov->iov_base, sizeof(*attr))) > + return -EFAULT; > + return 0; > +} > + > +static inline long mq_mmsg_done_or_error(unsigned int done, int ret) > +{ > + if (ret < 0 && done) > + return done; > + return ret; > +} > + > +static ssize_t do_mq_recvmmsg(mqd_t mqdes, const struct mq_mmsg_attrs *a= ttrs, > + unsigned int fla= gs, unsigned long start_idx, > + struct timespec6= 4 *ts, bool compat) > +{ > + struct iov_iter iter; > + struct iovec outer_iovstack[UIO_FASTIOV], *free_iov =3D outer_iov= stack; > + const struct iovec *msg_iov; > + ktime_t batch_deadline, *timeout =3D NULL; > + ssize_t ret =3D 0; > + ssize_t batch_success =3D 0; > + unsigned long index; > + unsigned int i; > + > + if (flags & (~MQ_VALID_FLAGS)) > + return -EINVAL; > + if ((flags & MQ_RECV) && (flags & MQ_PEEK)) > + return -EINVAL; > + if (start_idx > attrs->vlen) > + return -EINVAL; > + if (!attrs->vlen || attrs->vlen > UIO_MAXIOV) > + return -EINVAL; > + > + ret =3D __import_iovec(ITER_DEST, > + attrs->msg_attrs_vec, attrs->vlen, > + ARRAY_SIZE(outer_iovstack), &free_iov, &iter= , > + compat); > + if (ret < 0) > + return ret; > + msg_iov =3D iter_iov(&iter); > + > + /* One absolute deadline for the whole batch. */ > + if (ts) { > + batch_deadline =3D timespec64_to_ktime(*ts); > + timeout =3D &batch_deadline; > + } > + for (i =3D start_idx; i < attrs->vlen; i++) { > + struct mq_msg_attrs desc =3D {}; > + > + ret =3D mq_mmsg_copy_msg_attr(&desc, &msg_iov[i], compat)= ; > + if (ret < 0) > + break; > + > + index =3D (flags & MQ_PEEK) ? i : 0; > + ret =3D do_mq_recvmsg2(mqdes, &desc, flags, index, ts, ti= meout); > + > + if (ret < 0) { > + if (attrs->ret && put_user(ret, attrs->ret)) { > + kfree(free_iov); > + return -EFAULT; > + } > + break; > + } > + batch_success++; > + } > + > + if (!(batch_success !=3D attrs->vlen)) { > + if (attrs->ret && put_user(0, attrs->ret)) { > + kfree(free_iov); > + return -EFAULT; > + } > + } > + kfree(free_iov); > + return mq_mmsg_done_or_error(batch_success, ret < 0 ? ret : batch= _success); > +} > + > +static ssize_t do_mq_sendmmsg(mqd_t mqdes, const struct mq_mmsg_attrs *a= ttrs, > + unsigned long st= art_idx, struct timespec64 *ts, > + bool compat) > +{ > + struct iov_iter iter; > + struct iovec outer_iovstack[UIO_FASTIOV], *free_iov =3D outer_iov= stack; > + const struct iovec *msg_iov; > + ktime_t batch_deadline, *timeout =3D NULL; > + ssize_t ret =3D 0; > + ssize_t batch_success =3D 0; > + unsigned int i; > + > + if (!attrs->vlen || attrs->vlen > UIO_MAXIOV || start_idx > attrs= ->vlen) > + return -EINVAL; > + > + ret =3D __import_iovec(ITER_SOURCE, > + attrs->msg_attrs_vec, attrs->vlen, > + ARRAY_SIZE(outer_iovstack), &free_iov, &iter= , > + compat); > + if (ret < 0) > + return ret; > + msg_iov =3D iter_iov(&iter); > + > + if (ts) { > + batch_deadline =3D timespec64_to_ktime(*ts); > + timeout =3D &batch_deadline; > + } > + for (i =3D start_idx; i < attrs->vlen; i++) { > + struct mq_msg_attrs desc =3D {}; > + unsigned int msg_prio =3D 0; > + > + ret =3D mq_mmsg_copy_msg_attr(&desc, &msg_iov[i], compat)= ; > + if (ret < 0) > + break; > + if (!desc.msg_prio) { > + ret =3D -EINVAL; > + break; > + } > + if (get_user(msg_prio, desc.msg_prio)) { > + ret =3D -EFAULT; > + break; > + } > + ret =3D do_mq_sendmsg(mqdes, desc.msg_ptr, desc.msg_len, > + msg_prio, ts, timeout)= ; > + > + if (ret < 0) { > + if (attrs->ret && put_user(ret, attrs->ret)) { > + kfree(free_iov); > + return -EFAULT; > + } > + break; > + } > + batch_success++; > + } > + > + if (!(batch_success !=3D attrs->vlen)) { > + if (attrs->ret && put_user(0, attrs->ret)) { > + kfree(free_iov); > + return -EFAULT; > + } > + } > + kfree(free_iov); > + return mq_mmsg_done_or_error(batch_success, ret < 0 ? ret : batch= _success); > +} > + > SYSCALL_DEFINE5(mq_timedsend, mqd_t, mqdes, const char __user *, u_msg_p= tr, > - size_t, msg_len, unsigned int, msg_prio, > - const struct __kernel_timespec __user *, u_abs_timeout) > + size_t, msg_len, unsigned int, msg_prio, > + const struct __kernel_timespec __user *, u_abs= _timeout) > { > struct timespec64 ts, *p =3D NULL; > + > if (u_abs_timeout) { > int res =3D prepare_timeout(u_abs_timeout, &ts); > + > if (res) > return res; > p =3D &ts; > @@ -1244,13 +1646,36 @@ SYSCALL_DEFINE5(mq_timedsend, mqd_t, mqdes, const= char __user *, u_msg_ptr, > return do_mq_timedsend(mqdes, u_msg_ptr, msg_len, msg_prio, p); > } > > +SYSCALL_DEFINE5(mq_sendmmsg, mqd_t, mqdes, struct mq_mmsg_attrs __user *= , attrs, > + unsigned int, attrs_len, unsigned long, start_= idx, > + const struct __kernel_timespec __user *, u_abs= _timeout) > +{ > + struct mq_mmsg_attrs kattrs =3D {}; > + struct timespec64 ts, *p =3D NULL; > + int ret; > + > + ret =3D mq_mmsg_copy_attrs_from_user(&kattrs, attrs, attrs_len); > + if (ret) > + return ret; > + if (u_abs_timeout) { > + int res =3D prepare_timeout(u_abs_timeout, &ts); > + > + if (res) > + return res; > + p =3D &ts; > + } > + return do_mq_sendmmsg(mqdes, &kattrs, start_idx, p, false); > +} > + > SYSCALL_DEFINE5(mq_timedreceive, mqd_t, mqdes, char __user *, u_msg_ptr, > - size_t, msg_len, unsigned int __user *, u_msg_prio, > - const struct __kernel_timespec __user *, u_abs_timeout) > + size_t, msg_len, unsigned int __user *, u_msg_= prio, > + const struct __kernel_timespec __user *, u_abs= _timeout) > { > struct timespec64 ts, *p =3D NULL; > + > if (u_abs_timeout) { > int res =3D prepare_timeout(u_abs_timeout, &ts); > + > if (res) > return res; > p =3D &ts; > @@ -1258,6 +1683,26 @@ SYSCALL_DEFINE5(mq_timedreceive, mqd_t, mqdes, cha= r __user *, u_msg_ptr, > return do_mq_timedreceive(mqdes, u_msg_ptr, msg_len, u_msg_prio, = p); > } > > +SYSCALL_DEFINE6(mq_recvmmsg, mqd_t, mqdes, struct mq_mmsg_attrs __user *= , attrs, > + unsigned int, attrs_len, unsigned int, flags, = unsigned long, start_idx, > + const struct __kernel_timespec __user *, u_abs= _timeout) > +{ > + struct mq_mmsg_attrs kattrs =3D {}; > + struct timespec64 ts, *p =3D NULL; > + int ret; > + > + ret =3D mq_mmsg_copy_attrs_from_user(&kattrs, attrs, attrs_len); > + if (ret) > + return ret; > + if (u_abs_timeout) { > + int res =3D prepare_timeout(u_abs_timeout, &ts); > + > + if (res) > + return res; > + p =3D &ts; > + } > + return do_mq_recvmmsg(mqdes, &kattrs, flags, start_idx, p, false)= ; > +} > /* > * Notes: the case when user wants us to deregister (with NULL as pointe= r) > * and he isn't currently owner of notification, will be silently discar= ded. > @@ -1460,7 +1905,7 @@ struct compat_mq_attr { > }; > > static inline int get_compat_mq_attr(struct mq_attr *attr, > - const struct compat_mq_attr __user *uattr) > + c= onst struct compat_mq_attr __user *uattr) > { > struct compat_mq_attr v; > > @@ -1476,7 +1921,7 @@ static inline int get_compat_mq_attr(struct mq_attr= *attr, > } > > static inline int put_compat_mq_attr(const struct mq_attr *attr, > - struct compat_mq_attr __user *uattr) > + s= truct compat_mq_attr __user *uattr) > { > struct compat_mq_attr v; > > @@ -1541,11 +1986,61 @@ COMPAT_SYSCALL_DEFINE3(mq_getsetattr, mqd_t, mqde= s, > return -EFAULT; > return 0; > } > + > +COMPAT_SYSCALL_DEFINE5(mq_sendmmsg, mqd_t, mqdes, struct compat_mq_mmsg_= attrs __user *, attrs, > + unsigned int, attrs_len, unsign= ed long, start_idx, > + const struct __kernel_timespec = __user *, u_abs_timeout) > +{ > + struct mq_mmsg_attrs kattrs =3D {}; > + struct timespec64 ts, *p =3D NULL; > + struct iovec msg_atrrs_vec =3D {}; > + int ret; > + > + kattrs.msg_attrs_vec =3D &msg_atrrs_vec; > + ret =3D mq_mmsg_copy_compat_attrs(&kattrs, attrs, attrs_len); > + if (ret) > + return ret; > + > + if (u_abs_timeout) { > + int res =3D prepare_timeout(u_abs_timeout, &ts); > + > + if (res) > + return res; > + p =3D &ts; > + } > + > + return do_mq_sendmmsg(mqdes, &kattrs, start_idx, p, true); > +} > + > +COMPAT_SYSCALL_DEFINE6(mq_recvmmsg, mqd_t, mqdes, struct compat_mq_mmsg_= attrs __user *, attrs, > + unsigned int, attrs_len, unsign= ed int, flags, unsigned long, start_idx, > + const struct __kernel_timespec = __user *, u_abs_timeout) > +{ > + struct mq_mmsg_attrs kattrs =3D {}; > + struct timespec64 ts, *p =3D NULL; > + struct iovec msg_atrrs_vec =3D {}; > + int ret; > + > + kattrs.msg_attrs_vec =3D &msg_atrrs_vec; > + ret =3D mq_mmsg_copy_compat_attrs(&kattrs, attrs, attrs_len); > + if (ret) > + return ret; > + > + if (u_abs_timeout) { > + int res =3D prepare_timeout(u_abs_timeout, &ts); > + > + if (res) > + return res; > + p =3D &ts; > + } > + > + return do_mq_recvmmsg(mqdes, &kattrs, flags, start_idx, p, true); > +} > #endif > > #ifdef CONFIG_COMPAT_32BIT_TIME > static int compat_prepare_timeout(const struct old_timespec32 __user *p, > - struct timespec64 *ts) > + struct t= imespec64 *ts) > { > if (get_old_timespec32(ts, p)) > return -EFAULT; > @@ -1560,8 +2055,10 @@ SYSCALL_DEFINE5(mq_timedsend_time32, mqd_t, mqdes, > const struct old_timespec32 __user *, u_abs_timeout) > { > struct timespec64 ts, *p =3D NULL; > + > if (u_abs_timeout) { > int res =3D compat_prepare_timeout(u_abs_timeout, &ts); > + > if (res) > return res; > p =3D &ts; > @@ -1575,14 +2072,17 @@ SYSCALL_DEFINE5(mq_timedreceive_time32, mqd_t, mq= des, > const struct old_timespec32 __user *, u_abs_timeout) > { > struct timespec64 ts, *p =3D NULL; > + > if (u_abs_timeout) { > int res =3D compat_prepare_timeout(u_abs_timeout, &ts); > + > if (res) > return res; > p =3D &ts; > } > return do_mq_timedreceive(mqdes, u_msg_ptr, msg_len, u_msg_prio, = p); > } > + > #endif > > static const struct inode_operations mqueue_dir_inode_operations =3D { > diff --git a/ipc/msg.c b/ipc/msg.c > index 62996b97f0ac..6392b11dd7f7 100644 > --- a/ipc/msg.c > +++ b/ipc/msg.c > @@ -1156,7 +1156,7 @@ static long do_msgrcv(int msqid, void __user *buf, = size_t bufsz, long msgtyp, in > * not update queue parameters. > */ > if (msgflg & MSG_COPY) { > - msg =3D copy_msg(msg, copy); > + msg =3D copy_msg(msg, copy, msg->m_ts); > goto out_unlock0; > } > > diff --git a/ipc/msgutil.c b/ipc/msgutil.c > index e28f0cecb2ec..f2854fd8d7a4 100644 > --- a/ipc/msgutil.c > +++ b/ipc/msgutil.c > @@ -51,7 +51,7 @@ static int __init init_msg_buckets(void) > } > subsys_initcall(init_msg_buckets); > > -static struct msg_msg *alloc_msg(size_t len) > +struct msg_msg *alloc_msg(size_t len) > { > struct msg_msg *msg; > struct msg_msgseg **pseg; > @@ -122,39 +122,37 @@ struct msg_msg *load_msg(const void __user *src, si= ze_t len) > free_msg(msg); > return ERR_PTR(err); > } > -#ifdef CONFIG_CHECKPOINT_RESTORE > -struct msg_msg *copy_msg(struct msg_msg *src, struct msg_msg *dst) > + > +struct msg_msg *copy_msg(struct msg_msg *src, struct msg_msg *dst, size_= t len) > { > - struct msg_msgseg *dst_pseg, *src_pseg; > - size_t len =3D src->m_ts; > - size_t alen; > + struct msg_msgseg *src_seg, *dst_seg; > + size_t remaining, chunk; > > - if (src->m_ts > dst->m_ts) > + if (len > src->m_ts) > return ERR_PTR(-EINVAL); > > - alen =3D min(len, DATALEN_MSG); > - memcpy(dst + 1, src + 1, alen); > + chunk =3D min(len, DATALEN_MSG); > > - for (dst_pseg =3D dst->next, src_pseg =3D src->next; > - src_pseg !=3D NULL; > - dst_pseg =3D dst_pseg->next, src_pseg =3D src_pseg->next) { > + memcpy(dst + 1, src + 1, chunk); > + remaining =3D len - chunk; > + src_seg =3D src->next; > + dst_seg =3D dst->next; > > - len -=3D alen; > - alen =3D min(len, DATALEN_SEG); > - memcpy(dst_pseg + 1, src_pseg + 1, alen); > + while (remaining > 0 && src_seg && dst_seg) { > + chunk =3D min(remaining, DATALEN_SEG); > + memcpy(dst_seg + 1, src_seg + 1, chunk); > + remaining -=3D chunk; > + src_seg =3D src_seg->next; > + dst_seg =3D dst_seg->next; > } > - > + if (remaining !=3D 0) > + return ERR_PTR(-EINVAL); > dst->m_type =3D src->m_type; > - dst->m_ts =3D src->m_ts; > - > + dst->m_ts =3D len; > return dst; > -} > -#else > -struct msg_msg *copy_msg(struct msg_msg *src, struct msg_msg *dst) > -{ > - return ERR_PTR(-ENOSYS); > -} > -#endif > + > + } > + > int store_msg(void __user *dest, struct msg_msg *msg, size_t len) > { > size_t alen; > diff --git a/ipc/util.h b/ipc/util.h > index a55d6cebe6d3..374abeee79b3 100644 > --- a/ipc/util.h > +++ b/ipc/util.h > @@ -197,8 +197,9 @@ int ipc_parse_version(int *cmd); > > extern void free_msg(struct msg_msg *msg); > extern struct msg_msg *load_msg(const void __user *src, size_t len); > -extern struct msg_msg *copy_msg(struct msg_msg *src, struct msg_msg *dst= ); > +extern struct msg_msg *copy_msg(struct msg_msg *src, struct msg_msg *dst= , size_t len); > extern int store_msg(void __user *dest, struct msg_msg *msg, size_t len)= ; > +extern struct msg_msg *alloc_msg(size_t len); > > static inline int ipc_checkid(struct kern_ipc_perm *ipcp, int id) > { > diff --git a/kernel/sys_ni.c b/kernel/sys_ni.c > index add3032da16f..8219d76c72a0 100644 > --- a/kernel/sys_ni.c > +++ b/kernel/sys_ni.c > @@ -392,5 +392,11 @@ COND_SYSCALL(setuid16); > COND_SYSCALL(rseq); > COND_SYSCALL(rseq_slice_yield); > > +/* ipc */ > +COND_SYSCALL(mq_recvmmsg); > +COND_SYSCALL_COMPAT(mq_recvmmsg); > +COND_SYSCALL(mq_sendmmsg); > +COND_SYSCALL_COMPAT(mq_sendmmsg); > + > COND_SYSCALL(uretprobe); > COND_SYSCALL(uprobe); > -- > 2.43.0 > >