* [PATCH v3 1/4] IPC: Added two new system call mq_recvmmsg() and mq_sendmmsg()
2026-06-12 5:20 [PATCH v3 0/4] Add two new system call mq_recvmmsg() and mq_sendmmsg() to posix ipc mqueue Mathura_Kumar
@ 2026-06-12 5:20 ` Mathura_Kumar
2026-06-12 11:49 ` Pavel Tikhomirov
0 siblings, 1 reply; 12+ messages in thread
From: Mathura_Kumar @ 2026-06-12 5:20 UTC (permalink / raw)
To: criu; +Cc: academic1mathura, avagin
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 discrete
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
Signed-off-by: Mathura_Kumar <academic1mathura@gmail.com>
---
include/linux/compat.h | 9 +-
include/linux/syscalls.h | 8 +
include/uapi/asm-generic/unistd.h | 9 +-
include/uapi/linux/mqueue.h | 27 +-
ipc/mqueue.c | 506 ++++++++++++++++++++++++++++--
ipc/msg.c | 2 +-
ipc/msgutil.c | 51 ++-
ipc/util.h | 3 +-
kernel/sys_ni.c | 6 +
9 files changed, 558 insertions(+), 63 deletions(-)
diff --git a/include/linux/compat.h b/include/linux/compat.h
index 8da0a15c95f4..200d6d9b80e5 100644
--- a/include/linux/compat.h
+++ b/include/linux/compat.h
@@ -22,6 +22,7 @@
#include <asm/compat.h>
#include <asm/siginfo.h>
#include <asm/signal.h>
+#include <linux/mqueue.h>
#ifdef CONFIG_ARCH_HAS_SYSCALL_WRAPPER
/*
@@ -805,8 +806,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_mmsg_attrs __user *attrs,
+ unsigned int attrs_len, unsigned int flags, unsigned long start_index,
+ const struct __kernel_timespec __user *abs_timeout);
+asmlinkage long compat_sys_mq_recvmmsg(mqd_t mqdes, struct compat_mq_mmsg_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 >= 1 arch
diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h
index 4fb7291f54b6..b3297f9dd126 100644
--- a/include/linux/syscalls.h
+++ b/include/linux/syscalls.h
@@ -79,6 +79,7 @@ struct mnt_id_req;
struct ns_id_req;
struct xattr_args;
struct file_attr;
+struct mq_mmsg_attrs;
#include <linux/types.h>
#include <linux/aio_abi.h>
@@ -93,6 +94,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 +741,13 @@ asmlinkage long sys_sysinfo(struct sysinfo __user *info);
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 __user *abs_timeout);
+asmlinkage long sys_mq_sendmmsg(mqd_t mqdes, struct mq_mmsg_attrs __user *attrs,
+ unsigned int attrs_len, unsigned int flags, unsigned long start_index,
+ const struct __kernel_timespec __user *abs_timeout);
asmlinkage long sys_mq_timedreceive(mqd_t mqdes, char __user *msg_ptr, size_t msg_len, unsigned int __user *msg_prio, const struct __kernel_timespec __user *abs_timeout);
+asmlinkage long sys_mq_recvmmsg(mqd_t mqdes, struct mq_mmsg_attrs __user *attrs,
+ unsigned int attrs_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 __user *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..9b2e8539472c 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 msg_prio;
+ compat_uptr_t msg_ptr;
+};
+
+struct compat_mq_mmsg_attrs {
+ struct compat_iovec __user *msg_attrs_vec;
+ compat_size_t vlen;
+ compat_uptr_t ret;
+};
+
/*
* SIGEV_THREAD implementation:
* SIGEV_THREAD must be implemented in user space. If SIGEV_THREAD is passed
diff --git a/ipc/mqueue.c b/ipc/mqueue.c
index 4798b375972b..e3e83eb9e7bc 100644
--- a/ipc/mqueue.c
+++ b/ipc/mqueue.c
@@ -12,6 +12,8 @@
* Audit: George Wilson (ltcgcw@us.ibm.com)
*/
+#include "linux/compat.h"
+#include "linux/types.h"
#include <linux/capability.h>
#include <linux/init.h>
#include <linux/pagemap.h>
@@ -38,6 +40,8 @@
#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 "util.h"
@@ -54,6 +58,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
@@ -1034,16 +1042,15 @@ 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 int 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 = NULL;
struct posix_msg_tree_node *new_leaf = NULL;
int ret = 0;
DEFINE_WAKE_Q(wake_q);
@@ -1051,11 +1058,6 @@ static int do_mq_timedsend(mqd_t mqdes, const char __user *u_msg_ptr,
if (unlikely(msg_prio >= (unsigned long) MQ_PRIO_MAX))
return -EINVAL;
- if (ts) {
- expires = timespec64_to_ktime(*ts);
- timeout = &expires;
- }
-
audit_mq_sendrecv(mqdes, msg_len, msg_prio, ts);
CLASS(fd, f)(mqdes);
@@ -1139,23 +1141,31 @@ static int do_mq_timedsend(mqd_t mqdes, const char __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 int do_mq_timedsend(mqd_t mqdes, const char __user *u_msg_ptr,
+ size_t msg_len, unsigned 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 = NULL;
- struct posix_msg_tree_node *new_leaf = NULL;
if (ts) {
expires = timespec64_to_ktime(*ts);
timeout = &expires;
}
+ return do_mq_sendmsg(mqdes, u_msg_ptr, msg_len, msg_prio, ts, timeout);
+}
+
+static int do_mq_recvmsg(mqd_t mqdes, char __user *u_msg_ptr, size_t msg_len,
+ unsigned int __user *u_msg_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 = NULL;
+
audit_mq_sendrecv(mqdes, msg_len, 0, ts);
CLASS(fd, f)(mqdes);
@@ -1230,6 +1240,350 @@ static int do_mq_timedreceive(mqd_t mqdes, char __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)
+{
+ ktime_t expires, *timeout = NULL;
+
+ if (ts) {
+ expires = timespec64_to_ktime(*ts);
+ timeout = &expires;
+ }
+
+ return do_mq_recvmsg(mqdes, u_msg_ptr, msg_len,
+ u_msg_prio, ts, timeout);
+}
+
+static struct msg_msg *mq_peek_index(struct mqueue_inode_info *info, unsigned long index)
+{
+ struct rb_node *node;
+ struct posix_msg_tree_node *leaf;
+ struct msg_msg *msg;
+
+ int count = 0;
+
+ /* Start from highest priority */
+ node = rb_last(&info->msg_tree);
+ while (node) {
+ leaf = rb_entry(node, struct posix_msg_tree_node, rb_node);
+ list_for_each_entry(msg, &leaf->msg_list, m_list) {
+ if (count == index)
+ return msg;
+ count++;
+ }
+
+ node = rb_prev(node);
+ }
+
+ return NULL;
+}
+
+static int do_mq_recvmsg2(mqd_t mqdes, struct mq_msg_attrs *args, unsigned int flags,
+ unsigned long index, const 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 = file_inode(fd_file(f));
+ if (unlikely(fd_file(f)->f_op != &mqueue_file_operations))
+ return -EBADF;
+ info = 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 >= (unsigned long)info->attr.mq_maxmsg)
+ return -EINVAL;
+
+ spin_lock(&info->lock);
+ if (info->attr.mq_curmsgs == 0) {
+ spin_unlock(&info->lock);
+ return -EAGAIN;
+ }
+ msg_ptr = mq_peek_index(info, index);
+ if (!msg_ptr) {
+ spin_unlock(&info->lock);
+ return -ENODATA;
+ }
+ k_m_type = msg_ptr->m_type;
+ k_m_ts = msg_ptr->m_ts;
+ spin_unlock(&info->lock);
+
+ k_msg_buffer = alloc_msg(k_m_ts);
+ if (!k_msg_buffer)
+ return -ENOMEM;
+ ret = security_msg_msg_alloc(k_msg_buffer);
+ if (ret)
+ return ret;
+
+ /*
+ * Two spin locks are necessary here. We are avoiding atomic memory
+ * allocation and premature allocation before confirming
+ * a message actually exists to peek and retrieving required buffer size
+ * when first lock was taken.
+ */
+ spin_lock(&info->lock);
+ msg_ptr = mq_peek_index(info, index);
+ if (!msg_ptr || msg_ptr->m_type != k_m_type ||
+ msg_ptr->m_ts != k_m_ts) {
+ spin_unlock(&info->lock);
+ free_msg(k_msg_buffer);
+ return -EAGAIN;
+ }
+ msg_ptr = 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 = 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,
+ const struct compat_mq_mmsg_attrs __user *uattrs,
+ unsigned int attrs_len)
+{
+ struct compat_mq_mmsg_attrs v = {};
+ int err;
+
+ if (unlikely(attrs_len < sizeof(v)))
+ return -EINVAL;
+ if (unlikely(attrs_len > PAGE_SIZE))
+ return -E2BIG;
+ err = copy_struct_from_user(&v, sizeof(v), uattrs, attrs_len);
+ if (err)
+ return err;
+
+ memset(attrs, 0, sizeof(*attrs));
+ attrs->msg_attrs_vec->iov_base = compat_ptr(v.msg_attrs_vec->iov_base);
+ attrs->msg_attrs_vec->iov_len = v.msg_attrs_vec->iov_len;
+ attrs->ret = (int *)compat_ptr(v.ret);
+ attrs->vlen = 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 = {};
+
+ if (desc_iov->iov_len != sizeof(v))
+ return -EINVAL;
+ if (copy_from_user(&v, desc_iov->iov_base, sizeof(v)))
+ return -EFAULT;
+
+ memset(attr, 0, sizeof(*attr));
+ attr->msg_len = v.msg_len;
+ attr->msg_prio = (unsigned int *)compat_ptr(v.msg_prio);
+ attr->msg_ptr = compat_ptr(v.msg_ptr);
+ return 0;
+ }
+
+#endif
+
+static int mq_mmsg_copy_msg_attr(struct mq_msg_attrs *attr,
+ const struct iovec *desc_iov, bool compat)
+{
+ if (compat) {
+ return mq_mmsg_copy_compat_msg_attr(attr, desc_iov);
+ }
+
+ if (desc_iov->iov_len != sizeof(*attr))
+ return -EINVAL;
+ if (copy_from_user(attr, desc_iov->iov_base, sizeof(*attr)))
+ return -EFAULT;
+
+ return 0;
+}
+
+static inline int 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 *attrs,
+ unsigned int flags, unsigned long start_idx,
+ struct timespec64 *ts, bool compat)
+{
+ struct iov_iter iter;
+ struct iovec outer_iovstack[UIO_FASTIOV], *free_iov = outer_iovstack;
+ const struct iovec *msg_iov;
+ ktime_t batch_deadline, *timeout = NULL;
+ ssize_t ret = 0;
+ ssize_t batch_success = 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 = __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 = iter_iov(&iter);
+
+ /* One absolute deadline for the whole batch. */
+ if (ts) {
+ batch_deadline = timespec64_to_ktime(*ts);
+ timeout = &batch_deadline;
+ }
+
+ for (i = start_idx; i < attrs->vlen; i++) {
+ struct mq_msg_attrs desc = {};
+
+ ret = mq_mmsg_copy_msg_attr(&desc, &msg_iov[i], compat);
+ if (ret < 0)
+ break;
+
+ index = (flags & MQ_PEEK) ? i : 0;
+ ret = do_mq_recvmsg2(mqdes, &desc, flags, index, ts, timeout);
+
+ if (ret < 0) {
+ if (attrs->ret && put_user(ret, attrs->ret)) {
+ kfree(free_iov);
+ return -EFAULT;
+ }
+ break;
+ }
+ batch_success++;
+ }
+
+ if (!(batch_success != 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 *attrs,
+ unsigned long start_idx, struct timespec64 *ts,
+ bool compat)
+{
+ struct iov_iter iter;
+ struct iovec outer_iovstack[UIO_FASTIOV], *free_iov = outer_iovstack;
+ const struct iovec *msg_iov;
+ ktime_t batch_deadline, *timeout = NULL;
+ ssize_t ret = 0;
+ ssize_t batch_success = 0;
+ unsigned int i;
+
+ if (!attrs->vlen || attrs->vlen > UIO_MAXIOV || start_idx > attrs->vlen)
+ return -EINVAL;
+
+ ret = __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 = iter_iov(&iter);
+
+ if (ts) {
+ batch_deadline = timespec64_to_ktime(*ts);
+ timeout = &batch_deadline;
+ }
+
+ for (i = start_idx; i < attrs->vlen; i++) {
+ struct mq_msg_attrs desc = {};
+ unsigned int msg_prio = 0;
+
+ ret = mq_mmsg_copy_msg_attr(&desc, &msg_iov[i], compat);
+ if (ret < 0)
+ break;
+
+ if (!desc.msg_prio) {
+ ret = -EINVAL;
+ break;
+ }
+ if (get_user(msg_prio, desc.msg_prio)) {
+ ret = -EFAULT;
+ break;
+ }
+
+ ret = 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 != 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_ptr,
size_t, msg_len, unsigned int, msg_prio,
const struct __kernel_timespec __user *, u_abs_timeout)
@@ -1244,6 +1598,27 @@ 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 = {};
+ struct timespec64 ts, *p = NULL;
+ int ret;
+
+ ret = mq_mmsg_copy_attrs_from_user(&kattrs, attrs, attrs_len);
+ if (ret)
+ return ret;
+ if (u_abs_timeout) {
+ int res = prepare_timeout(u_abs_timeout, &ts);
+
+ if (res)
+ return res;
+ p = &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)
@@ -1251,6 +1626,7 @@ SYSCALL_DEFINE5(mq_timedreceive, mqd_t, mqdes, char __user *, u_msg_ptr,
struct timespec64 ts, *p = NULL;
if (u_abs_timeout) {
int res = prepare_timeout(u_abs_timeout, &ts);
+
if (res)
return res;
p = &ts;
@@ -1258,6 +1634,26 @@ SYSCALL_DEFINE5(mq_timedreceive, mqd_t, mqdes, char __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 = {};
+ struct timespec64 ts, *p = NULL;
+ int ret;
+
+ ret = mq_mmsg_copy_attrs_from_user(&kattrs, attrs, attrs_len);
+ if (ret)
+ return ret;
+ if (u_abs_timeout) {
+ int res = prepare_timeout(u_abs_timeout, &ts);
+
+ if (res)
+ return res;
+ p = &ts;
+ }
+ return do_mq_recvmmsg(mqdes, &kattrs, flags, start_idx, p, false);
+}
/*
* Notes: the case when user wants us to deregister (with NULL as pointer)
* and he isn't currently owner of notification, will be silently discarded.
@@ -1451,6 +1847,67 @@ SYSCALL_DEFINE3(mq_getsetattr, mqd_t, mqdes,
#ifdef CONFIG_COMPAT
+COMPAT_SYSCALL_DEFINE5(mq_sendmmsg, mqd_t, mqdes, struct compat_mq_mmsg_attrs __user *, attrs,
+ unsigned int, attrs_len, unsigned int, flags,
+ const struct __kernel_timespec __user *, u_abs_timeout)
+{
+ struct mq_mmsg_attrs kattrs = {};
+ struct timespec64 ts, *p = NULL;
+ int ret;
+
+ ret = mq_mmsg_copy_compat_attrs(&kattrs, attrs, attrs_len);
+ if (ret)
+ return ret;
+
+ if (u_abs_timeout) {
+ int res = prepare_timeout(u_abs_timeout, &ts);
+
+ if (res)
+ return res;
+ p = &ts;
+ }
+
+ return do_mq_sendmmsg(mqdes, &kattrs, flags, p, true);
+}
+
+COMPAT_SYSCALL_DEFINE6(mq_recvmmsg, mqd_t, mqdes, struct compat_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 = {};
+ struct timespec64 ts, *p = NULL;
+ int ret;
+
+ ret = mq_mmsg_copy_compat_attrs(&kattrs, attrs, attrs_len);
+ if (ret)
+ return ret;
+
+ if (u_abs_timeout) {
+ int res = prepare_timeout(u_abs_timeout, &ts);
+
+ if (res)
+ return res;
+ p = &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)
+{
+ if (get_old_timespec32(ts, p))
+ return -EFAULT;
+ if (!timespec64_valid(ts))
+ return -EINVAL;
+ return 0;
+}
+
+#ifdef CONFIG_COMPAT
+
struct compat_mq_attr {
compat_long_t mq_flags; /* message queue flags */
compat_long_t mq_maxmsg; /* maximum number of messages */
@@ -1541,18 +1998,8 @@ COMPAT_SYSCALL_DEFINE3(mq_getsetattr, mqd_t, mqdes,
return -EFAULT;
return 0;
}
-#endif
-#ifdef CONFIG_COMPAT_32BIT_TIME
-static int compat_prepare_timeout(const struct old_timespec32 __user *p,
- struct timespec64 *ts)
-{
- if (get_old_timespec32(ts, p))
- return -EFAULT;
- if (!timespec64_valid(ts))
- return -EINVAL;
- return 0;
-}
+#endif
SYSCALL_DEFINE5(mq_timedsend_time32, mqd_t, mqdes,
const char __user *, u_msg_ptr,
@@ -1583,6 +2030,7 @@ SYSCALL_DEFINE5(mq_timedreceive_time32, mqd_t, mqdes,
}
return do_mq_timedreceive(mqdes, u_msg_ptr, msg_len, u_msg_prio, p);
}
+
#endif
static const struct inode_operations mqueue_dir_inode_operations = {
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 = copy_msg(msg, copy);
+ msg = copy_msg(msg, copy, msg->m_ts);
goto out_unlock0;
}
diff --git a/ipc/msgutil.c b/ipc/msgutil.c
index e28f0cecb2ec..25729e96f83c 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,36 @@ struct msg_msg *load_msg(const void __user *src, size_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 = 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 = min(len, DATALEN_MSG);
- memcpy(dst + 1, src + 1, alen);
-
- for (dst_pseg = dst->next, src_pseg = src->next;
- src_pseg != NULL;
- dst_pseg = dst_pseg->next, src_pseg = src_pseg->next) {
-
- len -= alen;
- alen = min(len, DATALEN_SEG);
- memcpy(dst_pseg + 1, src_pseg + 1, alen);
+ chunk = min(len, DATALEN_MSG);
+ memcpy(dst + 1, src + 1, chunk);
+ remaining = len - chunk;
+ src_seg = src->next;
+ dst_seg = dst->next;
+ while (remaining > 0 && src_seg && dst_seg) {
+
+ chunk = min(remaining, DATALEN_SEG);
+ memcpy(dst_seg + 1, src_seg + 1, chunk);
+ remaining -= chunk;
+ src_seg = src_seg->next;
+ dst_seg = dst_seg->next;
}
+ if (remaining != 0)
+ return ERR_PTR(-EINVAL);
dst->m_type = src->m_type;
- dst->m_ts = src->m_ts;
-
+ dst->m_ts = src->m_ts;
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
^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCH v3 1/4] IPC: Added two new system call mq_recvmmsg() and mq_sendmmsg()
2026-06-12 5:20 ` [PATCH v3 1/4] IPC: Added two new system call mq_recvmmsg() and mq_sendmmsg() Mathura_Kumar
@ 2026-06-12 11:49 ` Pavel Tikhomirov
[not found] ` <CA+QNo20DfeHOVYq4XgyCaUU4-3AYxh+tDyymjCD1DLkw7zytrA@mail.gmail.com>
0 siblings, 1 reply; 12+ messages in thread
From: Pavel Tikhomirov @ 2026-06-12 11:49 UTC (permalink / raw)
To: Mathura_Kumar, criu; +Cc: avagin
Hi, please see inline review comments below:
On 6/12/26 07:20, Mathura_Kumar 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 discrete
> 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
>
> Signed-off-by: Mathura_Kumar <academic1mathura@gmail.com>
> ---
> include/linux/compat.h | 9 +-
> include/linux/syscalls.h | 8 +
> include/uapi/asm-generic/unistd.h | 9 +-
> include/uapi/linux/mqueue.h | 27 +-
> ipc/mqueue.c | 506 ++++++++++++++++++++++++++++--
> ipc/msg.c | 2 +-
> ipc/msgutil.c | 51 ++-
> ipc/util.h | 3 +-
> kernel/sys_ni.c | 6 +
> 9 files changed, 558 insertions(+), 63 deletions(-)
>
> diff --git a/include/linux/compat.h b/include/linux/compat.h
> index 8da0a15c95f4..200d6d9b80e5 100644
> --- a/include/linux/compat.h
> +++ b/include/linux/compat.h
> @@ -22,6 +22,7 @@
> #include <asm/compat.h>
> #include <asm/siginfo.h>
> #include <asm/signal.h>
> +#include <linux/mqueue.h>
>
> #ifdef CONFIG_ARCH_HAS_SYSCALL_WRAPPER
> /*
> @@ -805,8 +806,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_mmsg_attrs __user *attrs,
> + unsigned int attrs_len, unsigned int flags, unsigned long start_index,
nit: Alignment is a bit off (not only this place).
> A very commonly used style is to align descendants to a function open parenthesis.
https://www.kernel.org/doc/html/v5.8/process/coding-style.html#breaking-long-lines-and-strings
So preferably it should be aligned just after parenthesis:
compat_sys_mq_sendmmsg(mqd_t mqdes, ...
unsigned int attrs_len,
^^
note: Normally you use offset/8 tabs + offset%8 spaces for this.
> + const struct __kernel_timespec __user *abs_timeout);
> +asmlinkage long compat_sys_mq_recvmmsg(mqd_t mqdes, struct compat_mq_mmsg_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 >= 1 arch
> diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h
> index 4fb7291f54b6..b3297f9dd126 100644
> --- a/include/linux/syscalls.h
> +++ b/include/linux/syscalls.h
> @@ -79,6 +79,7 @@ struct mnt_id_req;
> struct ns_id_req;
> struct xattr_args;
> struct file_attr;
> +struct mq_mmsg_attrs;
>
> #include <linux/types.h>
> #include <linux/aio_abi.h>
> @@ -93,6 +94,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 +741,13 @@ asmlinkage long sys_sysinfo(struct sysinfo __user *info);
> 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 __user *abs_timeout);
> +asmlinkage long sys_mq_sendmmsg(mqd_t mqdes, struct mq_mmsg_attrs __user *attrs,
> + unsigned int attrs_len, unsigned int flags, unsigned long start_index,
> + const struct __kernel_timespec __user *abs_timeout);
> asmlinkage long sys_mq_timedreceive(mqd_t mqdes, char __user *msg_ptr, size_t msg_len, unsigned int __user *msg_prio, const struct __kernel_timespec __user *abs_timeout);
> +asmlinkage long sys_mq_recvmmsg(mqd_t mqdes, struct mq_mmsg_attrs __user *attrs,
> + unsigned int attrs_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 __user *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..9b2e8539472c 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 msg_prio;
> + compat_uptr_t msg_ptr;
> +};
> +
> +struct compat_mq_mmsg_attrs {
> + struct compat_iovec __user *msg_attrs_vec;
> + compat_size_t vlen;
> + compat_uptr_t ret;
> +};
> +
> /*
> * SIGEV_THREAD implementation:
> * SIGEV_THREAD must be implemented in user space. If SIGEV_THREAD is passed
> diff --git a/ipc/mqueue.c b/ipc/mqueue.c
> index 4798b375972b..e3e83eb9e7bc 100644
> --- a/ipc/mqueue.c
> +++ b/ipc/mqueue.c
> @@ -12,6 +12,8 @@
> * Audit: George Wilson (ltcgcw@us.ibm.com)
> */
>
> +#include "linux/compat.h"
> +#include "linux/types.h"
The "relative" include is likely unintentional here, please replace "" to <>.
> #include <linux/capability.h>
> #include <linux/init.h>
> #include <linux/pagemap.h>
> @@ -38,6 +40,8 @@
> #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 "util.h"
> @@ -54,6 +58,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
>
> @@ -1034,16 +1042,15 @@ 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 int 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 = NULL;
> struct posix_msg_tree_node *new_leaf = NULL;
> int ret = 0;
> DEFINE_WAKE_Q(wake_q);
> @@ -1051,11 +1058,6 @@ static int do_mq_timedsend(mqd_t mqdes, const char __user *u_msg_ptr,
> if (unlikely(msg_prio >= (unsigned long) MQ_PRIO_MAX))
> return -EINVAL;
>
> - if (ts) {
> - expires = timespec64_to_ktime(*ts);
> - timeout = &expires;
> - }
> -
> audit_mq_sendrecv(mqdes, msg_len, msg_prio, ts);
>
> CLASS(fd, f)(mqdes);
> @@ -1139,23 +1141,31 @@ static int do_mq_timedsend(mqd_t mqdes, const char __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 int do_mq_timedsend(mqd_t mqdes, const char __user *u_msg_ptr,
> + size_t msg_len, unsigned 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 = NULL;
> - struct posix_msg_tree_node *new_leaf = NULL;
>
> if (ts) {
> expires = timespec64_to_ktime(*ts);
> timeout = &expires;
> }
>
> + return do_mq_sendmsg(mqdes, u_msg_ptr, msg_len, msg_prio, ts, timeout);
It feels that timeout argument is not really required here, we can always
derive it when we need it from ts.
> +}
> +
> +static int do_mq_recvmsg(mqd_t mqdes, char __user *u_msg_ptr, size_t msg_len,
> + unsigned int __user *u_msg_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 = NULL;
> +
> audit_mq_sendrecv(mqdes, msg_len, 0, ts);
>
> CLASS(fd, f)(mqdes);
> @@ -1230,6 +1240,350 @@ static int do_mq_timedreceive(mqd_t mqdes, char __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)
> +{
> + ktime_t expires, *timeout = NULL;
> +
> + if (ts) {
> + expires = timespec64_to_ktime(*ts);
> + timeout = &expires;
> + }
> +
> + return do_mq_recvmsg(mqdes, u_msg_ptr, msg_len,
> + u_msg_prio, ts, timeout);
> +}
> +
> +static struct msg_msg *mq_peek_index(struct mqueue_inode_info *info, unsigned long index)
> +{
> + struct rb_node *node;
> + struct posix_msg_tree_node *leaf;
> + struct msg_msg *msg;
> +
> + int count = 0;
> +
> + /* Start from highest priority */
> + node = rb_last(&info->msg_tree);
> + while (node) {
> + leaf = rb_entry(node, struct posix_msg_tree_node, rb_node);
> + list_for_each_entry(msg, &leaf->msg_list, m_list) {
> + if (count == index)
> + return msg;
> + count++;
> + }
> +
> + node = rb_prev(node);
> + }
> +
> + return NULL;
> +}
> +
> +static int do_mq_recvmsg2(mqd_t mqdes, struct mq_msg_attrs *args, unsigned int flags,
> + unsigned long index, const 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 = file_inode(fd_file(f));
> + if (unlikely(fd_file(f)->f_op != &mqueue_file_operations))
> + return -EBADF;
> + info = 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 >= (unsigned long)info->attr.mq_maxmsg)
> + return -EINVAL;
> +
> + spin_lock(&info->lock);
> + if (info->attr.mq_curmsgs == 0) {
> + spin_unlock(&info->lock);
> + return -EAGAIN;
> + }
> + msg_ptr = mq_peek_index(info, index);
> + if (!msg_ptr) {
> + spin_unlock(&info->lock);
> + return -ENODATA;
> + }
> + k_m_type = msg_ptr->m_type;
> + k_m_ts = msg_ptr->m_ts;
> + spin_unlock(&info->lock);
> +
> + k_msg_buffer = alloc_msg(k_m_ts);
> + if (!k_msg_buffer)
> + return -ENOMEM;
> + ret = security_msg_msg_alloc(k_msg_buffer);
> + if (ret)
> + return ret;
> +
> + /*
> + * Two spin locks are necessary here. We are avoiding atomic memory
> + * allocation and premature allocation before confirming
> + * a message actually exists to peek and retrieving required buffer size
> + * when first lock was taken.
> + */
nit: bad alignment
> + spin_lock(&info->lock);
> + msg_ptr = mq_peek_index(info, index);
> + if (!msg_ptr || msg_ptr->m_type != k_m_type ||
> + msg_ptr->m_ts != k_m_ts) {
> + spin_unlock(&info->lock);
> + free_msg(k_msg_buffer);
> + return -EAGAIN;
> + }
> + msg_ptr = 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 = 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,
> + const struct compat_mq_mmsg_attrs __user *uattrs,
> + unsigned int attrs_len)
> +{
> + struct compat_mq_mmsg_attrs v = {};
> + int err;
> +
> + if (unlikely(attrs_len < sizeof(v)))
> + return -EINVAL;
> + if (unlikely(attrs_len > PAGE_SIZE))
> + return -E2BIG;
> + err = copy_struct_from_user(&v, sizeof(v), uattrs, attrs_len);
> + if (err)
> + return err;
> +
> + memset(attrs, 0, sizeof(*attrs));
Do we need this memset? In all callers kattrs is explicitly zero
initialized (with = {}), right?
> + attrs->msg_attrs_vec->iov_base = compat_ptr(v.msg_attrs_vec->iov_base);
The value of attrs->msg_attrs_vec is NULL, here kernel will crash with
nullpointer dereference.
Also v.msg_attrs_vec seems like a user pointer, you can't just dereference
it using _user primitives from kernel space.
Did you try to run it in compat?
> + attrs->msg_attrs_vec->iov_len = v.msg_attrs_vec->iov_len;
> + attrs->ret = (int *)compat_ptr(v.ret);
> + attrs->vlen = 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 = {};
> +
> + if (desc_iov->iov_len != sizeof(v))
> + return -EINVAL;
> + if (copy_from_user(&v, desc_iov->iov_base, sizeof(v)))
> + return -EFAULT;
> +
> + memset(attr, 0, sizeof(*attr));
> + attr->msg_len = v.msg_len;
> + attr->msg_prio = (unsigned int *)compat_ptr(v.msg_prio);
> + attr->msg_ptr = compat_ptr(v.msg_ptr);
> + return 0;
> + }
> +
> +#endif
> +
> +static int mq_mmsg_copy_msg_attr(struct mq_msg_attrs *attr,
> + const struct iovec *desc_iov, bool compat)
> +{
> + if (compat) {
> + return mq_mmsg_copy_compat_msg_attr(attr, desc_iov);
> + }
> +
> + if (desc_iov->iov_len != sizeof(*attr))
> + return -EINVAL;
> + if (copy_from_user(attr, desc_iov->iov_base, sizeof(*attr)))
> + return -EFAULT;
> +
> + return 0;
> +}
> +
> +static inline int 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 *attrs,
> + unsigned int flags, unsigned long start_idx,
> + struct timespec64 *ts, bool compat)
> +{
> + struct iov_iter iter;
> + struct iovec outer_iovstack[UIO_FASTIOV], *free_iov = outer_iovstack;
> + const struct iovec *msg_iov;
> + ktime_t batch_deadline, *timeout = NULL;
> + ssize_t ret = 0;
> + ssize_t batch_success = 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 = __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 = iter_iov(&iter);
> +
> + /* One absolute deadline for the whole batch. */
> + if (ts) {
> + batch_deadline = timespec64_to_ktime(*ts);
> + timeout = &batch_deadline;
> + }
> +
> + for (i = start_idx; i < attrs->vlen; i++) {
> + struct mq_msg_attrs desc = {};
> +
> + ret = mq_mmsg_copy_msg_attr(&desc, &msg_iov[i], compat);
> + if (ret < 0)
> + break;
> +
> + index = (flags & MQ_PEEK) ? i : 0;
> + ret = do_mq_recvmsg2(mqdes, &desc, flags, index, ts, timeout);
The search insidde do_mq_recvmsg2 -> mq_peek_index does not seem that optimized,
maybe we can preserve last rbtree node pointer somehow to make the search
more effective instead of walking full tree each time.
> +
> + if (ret < 0) {
> + if (attrs->ret && put_user(ret, attrs->ret)) {
> + kfree(free_iov);
> + return -EFAULT;
> + }
> + break;
> + }
> + batch_success++;
> + }
> +
> + if (!(batch_success != 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 *attrs,
> + unsigned long start_idx, struct timespec64 *ts,
> + bool compat)
> +{
> + struct iov_iter iter;
> + struct iovec outer_iovstack[UIO_FASTIOV], *free_iov = outer_iovstack;
> + const struct iovec *msg_iov;
> + ktime_t batch_deadline, *timeout = NULL;
> + ssize_t ret = 0;
> + ssize_t batch_success = 0;
> + unsigned int i;
> +
> + if (!attrs->vlen || attrs->vlen > UIO_MAXIOV || start_idx > attrs->vlen)
> + return -EINVAL;
> +
> + ret = __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 = iter_iov(&iter);
> +
> + if (ts) {
> + batch_deadline = timespec64_to_ktime(*ts);
> + timeout = &batch_deadline;
> + }
> +
> + for (i = start_idx; i < attrs->vlen; i++) {
> + struct mq_msg_attrs desc = {};
> + unsigned int msg_prio = 0;
> +
> + ret = mq_mmsg_copy_msg_attr(&desc, &msg_iov[i], compat);
> + if (ret < 0)
> + break;
> +
> + if (!desc.msg_prio) {
> + ret = -EINVAL;
> + break;
> + }
> + if (get_user(msg_prio, desc.msg_prio)) {
> + ret = -EFAULT;
> + break;
> + }
> +
> + ret = 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 != 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_ptr,
> size_t, msg_len, unsigned int, msg_prio,
> const struct __kernel_timespec __user *, u_abs_timeout)
> @@ -1244,6 +1598,27 @@ 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 = {};
> + struct timespec64 ts, *p = NULL;
> + int ret;
> +
> + ret = mq_mmsg_copy_attrs_from_user(&kattrs, attrs, attrs_len);
> + if (ret)
> + return ret;
> + if (u_abs_timeout) {
> + int res = prepare_timeout(u_abs_timeout, &ts);
> +
> + if (res)
> + return res;
> + p = &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)
> @@ -1251,6 +1626,7 @@ SYSCALL_DEFINE5(mq_timedreceive, mqd_t, mqdes, char __user *, u_msg_ptr,
> struct timespec64 ts, *p = NULL;
> if (u_abs_timeout) {
> int res = prepare_timeout(u_abs_timeout, &ts);
> +
> if (res)
> return res;
> p = &ts;
> @@ -1258,6 +1634,26 @@ SYSCALL_DEFINE5(mq_timedreceive, mqd_t, mqdes, char __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 = {};
> + struct timespec64 ts, *p = NULL;
> + int ret;
> +
> + ret = mq_mmsg_copy_attrs_from_user(&kattrs, attrs, attrs_len);
> + if (ret)
> + return ret;
> + if (u_abs_timeout) {
> + int res = prepare_timeout(u_abs_timeout, &ts);
> +
> + if (res)
> + return res;
> + p = &ts;
> + }
> + return do_mq_recvmmsg(mqdes, &kattrs, flags, start_idx, p, false);
> +}
> /*
> * Notes: the case when user wants us to deregister (with NULL as pointer)
> * and he isn't currently owner of notification, will be silently discarded.
> @@ -1451,6 +1847,67 @@ SYSCALL_DEFINE3(mq_getsetattr, mqd_t, mqdes,
>
> #ifdef CONFIG_COMPAT
>
> +COMPAT_SYSCALL_DEFINE5(mq_sendmmsg, mqd_t, mqdes, struct compat_mq_mmsg_attrs __user *, attrs,
> + unsigned int, attrs_len, unsigned int, flags,
> + const struct __kernel_timespec __user *, u_abs_timeout)
> +{
> + struct mq_mmsg_attrs kattrs = {};
> + struct timespec64 ts, *p = NULL;
> + int ret;
> +
> + ret = mq_mmsg_copy_compat_attrs(&kattrs, attrs, attrs_len);
> + if (ret)
> + return ret;
> +
> + if (u_abs_timeout) {
> + int res = prepare_timeout(u_abs_timeout, &ts);
> +
> + if (res)
> + return res;
> + p = &ts;
> + }
> +
> + return do_mq_sendmmsg(mqdes, &kattrs, flags, p, true);
> +}
> +
> +COMPAT_SYSCALL_DEFINE6(mq_recvmmsg, mqd_t, mqdes, struct compat_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 = {};
> + struct timespec64 ts, *p = NULL;
> + int ret;
> +
> + ret = mq_mmsg_copy_compat_attrs(&kattrs, attrs, attrs_len);
> + if (ret)
> + return ret;
> +
> + if (u_abs_timeout) {
> + int res = prepare_timeout(u_abs_timeout, &ts);
> +
> + if (res)
> + return res;
> + p = &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)
> +{
> + if (get_old_timespec32(ts, p))
> + return -EFAULT;
> + if (!timespec64_valid(ts))
> + return -EINVAL;
> + return 0;
> +}
Maybe we need #endif here?
> +
> +#ifdef CONFIG_COMPAT
> +
> struct compat_mq_attr {
> compat_long_t mq_flags; /* message queue flags */
> compat_long_t mq_maxmsg; /* maximum number of messages */
> @@ -1541,18 +1998,8 @@ COMPAT_SYSCALL_DEFINE3(mq_getsetattr, mqd_t, mqdes,
> return -EFAULT;
> return 0;
> }
> -#endif
>
> -#ifdef CONFIG_COMPAT_32BIT_TIME
> -static int compat_prepare_timeout(const struct old_timespec32 __user *p,
> - struct timespec64 *ts)
> -{
> - if (get_old_timespec32(ts, p))
> - return -EFAULT;
> - if (!timespec64_valid(ts))
> - return -EINVAL;
> - return 0;
> -}
> +#endif
>
> SYSCALL_DEFINE5(mq_timedsend_time32, mqd_t, mqdes,
> const char __user *, u_msg_ptr,
> @@ -1583,6 +2030,7 @@ SYSCALL_DEFINE5(mq_timedreceive_time32, mqd_t, mqdes,
> }
> return do_mq_timedreceive(mqdes, u_msg_ptr, msg_len, u_msg_prio, p);
> }
> +
> #endif
>
> static const struct inode_operations mqueue_dir_inode_operations = {
> 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 = copy_msg(msg, copy);
> + msg = copy_msg(msg, copy, msg->m_ts);
> goto out_unlock0;
> }
>
> diff --git a/ipc/msgutil.c b/ipc/msgutil.c
> index e28f0cecb2ec..25729e96f83c 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,36 @@ struct msg_msg *load_msg(const void __user *src, size_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 = 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 = min(len, DATALEN_MSG);
> - memcpy(dst + 1, src + 1, alen);
> -
> - for (dst_pseg = dst->next, src_pseg = src->next;
> - src_pseg != NULL;
> - dst_pseg = dst_pseg->next, src_pseg = src_pseg->next) {
> -
> - len -= alen;
> - alen = min(len, DATALEN_SEG);
> - memcpy(dst_pseg + 1, src_pseg + 1, alen);
> + chunk = min(len, DATALEN_MSG);
> + memcpy(dst + 1, src + 1, chunk);
> + remaining = len - chunk;
> + src_seg = src->next;
> + dst_seg = dst->next;
> + while (remaining > 0 && src_seg && dst_seg) {
> +
> + chunk = min(remaining, DATALEN_SEG);
> + memcpy(dst_seg + 1, src_seg + 1, chunk);
> + remaining -= chunk;
> + src_seg = src_seg->next;
> + dst_seg = dst_seg->next;
> }
>
> + if (remaining != 0)
> + return ERR_PTR(-EINVAL);
> dst->m_type = src->m_type;
> - dst->m_ts = src->m_ts;
> -
> + dst->m_ts = src->m_ts;
> return dst;
> -}
> -#else
> -struct msg_msg *copy_msg(struct msg_msg *src, struct msg_msg *dst)
> -{
> - return ERR_PTR(-ENOSYS);
> -}
> -#endif
> +
> + }
nit: excess newline above and strange } alignment.
> +
> 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);
--
Best regards, Pavel Tikhomirov
Senior Software Developer, Virtuozzo.
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v3 1/4] IPC: Added two new system call mq_recvmmsg() and mq_sendmmsg()
[not found] ` <CA+QNo20DfeHOVYq4XgyCaUU4-3AYxh+tDyymjCD1DLkw7zytrA@mail.gmail.com>
@ 2026-06-12 12:35 ` Mathura
2026-06-12 13:52 ` Pavel Tikhomirov
0 siblings, 1 reply; 12+ messages in thread
From: Mathura @ 2026-06-12 12:35 UTC (permalink / raw)
To: Pavel Tikhomirov; +Cc: criu, avagin
Hi,
I am resending this due to a mail server error.
I never tested in compat mode. I will do and look at all the points
mentioned above.
I will insist you to please do a complete review and kindly tell me if
any more such or any architecture specific issue exists.
Regarding optimization on target retrieval from tree, can you provide
any suggestion how we can minimize complete traversal required for
each time. if we maintain state how we know the current cache or
stored efficient node belongs to which system calls from userspace if
we assume many concurrent processes calling it.
Thanks,
Mathura
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v3 1/4] IPC: Added two new system call mq_recvmmsg() and mq_sendmmsg()
2026-06-12 12:35 ` Mathura
@ 2026-06-12 13:52 ` Pavel Tikhomirov
2026-06-14 14:37 ` Mathura
0 siblings, 1 reply; 12+ messages in thread
From: Pavel Tikhomirov @ 2026-06-12 13:52 UTC (permalink / raw)
To: Mathura; +Cc: criu, avagin
On 6/12/26 14:35, Mathura wrote:
> Hi,
>
> I am resending this due to a mail server error.
Sorry, I accidentally replied with html and broke the formating.
>
> I never tested in compat mode. I will do and look at all the points
> mentioned above.
> I will insist you to please do a complete review and kindly tell me if
> any more such or any architecture specific issue exists.
>
> Regarding optimization on target retrieval from tree, can you provide
> any suggestion how we can minimize complete traversal required for
> each time. if we maintain state how we know the current cache or
> stored efficient node belongs to which system calls from userspace if
> we assume many concurrent processes calling it.
My idea was to basically just have `struct rb_node **last = NULL;`
in do_mq_recvmmsg() and pass it down to mq_peek_index(), so that we can
start the walk from last instead of rb_last, just doing `node = rb_prev(last)`
instead of full tree walk. You should set node back to last after that.
Only thing we should be careful about is that info->msg_tree
should be sufficiently locked to avoid removal of last from the tree.
>
> Thanks,
> Mathura
--
Best regards, Pavel Tikhomirov
Senior Software Developer, Virtuozzo.
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v3 1/4] IPC: Added two new system call mq_recvmmsg() and mq_sendmmsg()
2026-06-12 13:52 ` Pavel Tikhomirov
@ 2026-06-14 14:37 ` Mathura
0 siblings, 0 replies; 12+ messages in thread
From: Mathura @ 2026-06-14 14:37 UTC (permalink / raw)
To: Pavel Tikhomirov; +Cc: criu, avagin
Hi, Pavel
so yeah I have some thought over optimization approach,
By saving last visited node to avoid complete traversal, we can not
hold lock till finish entire batch to save eviction of our stored node
so i thought why not version tree and monotonically increase during
any operation that modify tree like insert and deletion, and use
version to invalidate stale saved last visited node node but this
seems it not give us any fruitful benefits as it may cause lot of
invalidation that again fallback to complete traversal.
So the only long run average optimal way seems to be to augment tree
and achieve logarithmic complexity on tree traversal and linear for
list in the same multiple priorities by some extra memory cost per
node probably 4-8 byte with utilizing current padded struct node
layout.
overall augmentation seems great efficiency with slight memory cost
which is tiny in high throughput message exchange.
So, What do you think of it ??
Thanks,
Mathura
On Fri, 12 Jun 2026 at 19:22, Pavel Tikhomirov
<ptikhomirov@virtuozzo.com> wrote:
>
>
>
> On 6/12/26 14:35, Mathura wrote:
> > Hi,
> >
> > I am resending this due to a mail server error.
>
> Sorry, I accidentally replied with html and broke the formating.
>
> >
> > I never tested in compat mode. I will do and look at all the points
> > mentioned above.
> > I will insist you to please do a complete review and kindly tell me if
> > any more such or any architecture specific issue exists.
> >
> > Regarding optimization on target retrieval from tree, can you provide
> > any suggestion how we can minimize complete traversal required for
> > each time. if we maintain state how we know the current cache or
> > stored efficient node belongs to which system calls from userspace if
> > we assume many concurrent processes calling it.
>
> My idea was to basically just have `struct rb_node **last = NULL;`
> in do_mq_recvmmsg() and pass it down to mq_peek_index(), so that we can
> start the walk from last instead of rb_last, just doing `node = rb_prev(last)`
> instead of full tree walk. You should set node back to last after that.
> Only thing we should be careful about is that info->msg_tree
> should be sufficiently locked to avoid removal of last from the tree.
>
> >
> > Thanks,
> > Mathura
>
> --
> Best regards, Pavel Tikhomirov
> Senior Software Developer, Virtuozzo.
>
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v3 0/4] Add two new system call mq_recvmmsg() and mq_sendmmsg() to posix ipc mqueue
@ 2026-06-20 11:23 Mathura_Kumar
2026-06-20 11:23 ` [PATCH v3 1/4] IPC: Added two new system call mq_recvmmsg() and mq_sendmmsg() Mathura_Kumar
` (3 more replies)
0 siblings, 4 replies; 12+ messages in thread
From: Mathura_Kumar @ 2026-06-20 11:23 UTC (permalink / raw)
To: criu; +Cc: academic1mathura, avagin, ptikhomirov
Patch series overview:
1. Add New system call mq_recvmmsg(), mq_sendmmsg() and handler implementation.
2. Add system call number in all most common architectures.
3. Add entries in performance tools.
4. Prepared Documentation and test.
change since v2:
- iovec is introduced for arguments.
- since v2 does not contain batch operation and I introduced batch support now
in recv or peek, due to that I added one more system call to support
batch during send as well otherwise i don't see any benefites of having
batching in consumer side only.
- the underlying tree structure used to store messages is augmented by introducing
two additional fields on each node. This enables logarithmic-time complexity for
peek operations and improves the efficiency of other tree traversals.
- some flaws in compat entry path fixed
- styles and formating improved
- test updated
- documentation is updated appropriately.
- v2 Link: https://lore.kernel.org/linux-arch/20260320052340.6696-1-academic1mathura@gmail.com/T/#t
Thanks for reviewing.
Mathura_Kumar (4):
IPC: Added two new system call mq_recvmmsg() and mq_sendmmsg()
IPC: Added system call entry in all of most common architectures
IPC: Added system call entry in performance tool
Test: Added self-testing and documentation
Documentation/userspace-api/index.rst | 2 +
Documentation/userspace-api/mq_recvmmsg.rst | 205 +++++++
Documentation/userspace-api/mq_sendmmsg.rst | 168 ++++++
arch/alpha/kernel/syscalls/syscall.tbl | 2 +
arch/arm/tools/syscall.tbl | 2 +
arch/arm64/tools/syscall_32.tbl | 3 +
arch/m68k/kernel/syscalls/syscall.tbl | 2 +
arch/microblaze/kernel/syscalls/syscall.tbl | 2 +
arch/mips/kernel/syscalls/syscall_n32.tbl | 2 +
arch/mips/kernel/syscalls/syscall_n64.tbl | 3 +
arch/mips/kernel/syscalls/syscall_o32.tbl | 2 +
arch/parisc/kernel/syscalls/syscall.tbl | 2 +
arch/powerpc/kernel/syscalls/syscall.tbl | 2 +
arch/s390/kernel/syscalls/syscall.tbl | 3 +
arch/sh/kernel/syscalls/syscall.tbl | 2 +
arch/sparc/kernel/syscalls/syscall.tbl | 3 +
arch/x86/entry/syscalls/syscall_32.tbl | 2 +
arch/x86/entry/syscalls/syscall_64.tbl | 3 +-
arch/xtensa/kernel/syscalls/syscall.tbl | 2 +
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 | 562 +++++++++++++++++-
ipc/msg.c | 2 +-
ipc/msgutil.c | 43 +-
ipc/util.h | 3 +-
kernel/sys_ni.c | 6 +
scripts/syscall.tbl | 2 +
tools/include/uapi/asm-generic/unistd.h | 8 +-
.../arch/alpha/entry/syscalls/syscall.tbl | 4 +
.../perf/arch/arm/entry/syscalls/syscall.tbl | 3 +
.../arch/arm64/entry/syscalls/syscall_32.tbl | 3 +
.../arch/mips/entry/syscalls/syscall_n64.tbl | 2 +
.../arch/parisc/entry/syscalls/syscall.tbl | 3 +
.../arch/powerpc/entry/syscalls/syscall.tbl | 3 +
.../perf/arch/s390/entry/syscalls/syscall.tbl | 2 +
tools/perf/arch/sh/entry/syscalls/syscall.tbl | 2 +
.../arch/sparc/entry/syscalls/syscall.tbl | 2 +
.../arch/x86/entry/syscalls/syscall_32.tbl | 2 +
.../arch/x86/entry/syscalls/syscall_64.tbl | 2 +
.../arch/xtensa/entry/syscalls/syscall.tbl | 2 +
tools/scripts/syscall.tbl | 2 +
tools/testing/selftests/ipc/Makefile | 2 +-
tools/testing/selftests/ipc/mq_recvmmsg.c | 391 ++++++++++++
tools/testing/selftests/ipc/mq_sendmmsg.c | 360 +++++++++++
46 files changed, 1815 insertions(+), 66 deletions(-)
create mode 100644 Documentation/userspace-api/mq_recvmmsg.rst
create mode 100644 Documentation/userspace-api/mq_sendmmsg.rst
create mode 100644 tools/testing/selftests/ipc/mq_recvmmsg.c
create mode 100644 tools/testing/selftests/ipc/mq_sendmmsg.c
--
2.43.0
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v3 1/4] IPC: Added two new system call mq_recvmmsg() and mq_sendmmsg()
2026-06-20 11:23 [PATCH v3 0/4] Add two new system call mq_recvmmsg() and mq_sendmmsg() to posix ipc mqueue Mathura_Kumar
@ 2026-06-20 11:23 ` Mathura_Kumar
2026-06-20 20:29 ` Andrei Vagin
2026-06-20 11:23 ` [PATCH v3 2/4] IPC: Added system call entry in all of most common architectures Mathura_Kumar
` (2 subsequent siblings)
3 siblings, 1 reply; 12+ messages in thread
From: Mathura_Kumar @ 2026-06-20 11:23 UTC (permalink / raw)
To: criu; +Cc: academic1mathura, avagin, ptikhomirov
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 discrete
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
Signed-off-by: Mathura_Kumar <academic1mathura@gmail.com>
---
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 | 562 ++++++++++++++++++++++++++++--
ipc/msg.c | 2 +-
ipc/msgutil.c | 46 ++-
ipc/util.h | 3 +-
kernel/sys_ni.c | 6 +
9 files changed, 613 insertions(+), 64 deletions(-)
diff --git a/include/linux/compat.h b/include/linux/compat.h
index 8da0a15c95f4..83db83b2f74f 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_mmsg_attrs __user *attrs,
+ unsigned int attrs_len, unsigned int flags, unsigned long start_index,
+ const struct __kernel_timespec __user *abs_timeout);
+asmlinkage long compat_sys_mq_recvmmsg(mqd_t mqdes, struct compat_mq_mmsg_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 >= 1 arch
diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h
index 4fb7291f54b6..339dc87bfa94 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 *info);
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 __user *abs_timeout);
+asmlinkage long sys_mq_sendmmsg(mqd_t mqdes, struct mq_mmsg_attrs __user *attrs,
+ unsigned int attrs_len, unsigned int flags, unsigned long start_index,
+ const struct __kernel_timespec __user *abs_timeout);
asmlinkage long sys_mq_timedreceive(mqd_t mqdes, char __user *msg_ptr, size_t msg_len, unsigned int __user *msg_prio, const struct __kernel_timespec __user *abs_timeout);
+asmlinkage long sys_mq_recvmmsg(mqd_t mqdes, struct mq_mmsg_attrs __user *attrs,
+ unsigned int attrs_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 __user *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 passed
diff --git a/ipc/mqueue.c b/ipc/mqueue.c
index 4798b375972b..99c0aa3d9c9d 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 priority */
+ unsigned int subtree_msg_count; /* sum of messages in this node and all descendants */
};
/*
@@ -186,7 +194,41 @@ static struct ipc_namespace *get_ns_from_inode(struct 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)->subtree_msg_count;
+}
+
+static void msg_tree_propagate_subtree_msg_count(struct rb_node *node, struct rb_node *stop)
+{
+ while (node != stop) {
+ struct posix_msg_tree_node *leaf = rb_entry(node, struct posix_msg_tree_node, rb_node);
+ unsigned int new_count = leaf->msg_count +
+ get_subtree_count(node->rb_left) +
+ get_subtree_count(node->rb_right);
+ if (leaf->subtree_msg_count == new_count)
+ break;
+ leaf->subtree_msg_count = new_count;
+ node = 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 = rb_entry(old, struct posix_msg_tree_node, rb_node);
+ struct posix_msg_tree_node *new_leaf = rb_entry(new, struct posix_msg_tree_node, rb_node);
+
+ new_leaf->subtree_msg_count = old_leaf->subtree_msg_count;
+}
+
+static const struct rb_augment_callbacks msg_tree_callbacks = {
+ .propagate = msg_tree_propagate_subtree_msg_count,
+ .copy = msg_tree_copy_subtree_msg_count,
+ .rotate = msg_tree_propagate_subtree_msg_count,
+};
+
static int msg_insert(struct msg_msg *msg, struct mqueue_inode_info *info)
{
struct rb_node **p, *parent = NULL;
@@ -216,13 +258,19 @@ static int msg_insert(struct msg_msg *msg, struct mqueue_inode_info *info)
INIT_LIST_HEAD(&leaf->msg_list);
}
leaf->priority = msg->m_type;
+ leaf->msg_count = 1;
+ leaf->subtree_msg_count = 1;
if (rightmost)
info->msg_tree_rightmost = &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_callbacks);
+ 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 += 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_tree_node *leaf,
if (info->msg_tree_rightmost == node)
info->msg_tree_rightmost = 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 = 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_node, 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] = 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 = NULL;
struct posix_msg_tree_node *new_leaf = NULL;
- int ret = 0;
+ ssize_t ret = 0;
DEFINE_WAKE_Q(wake_q);
if (unlikely(msg_prio >= (unsigned long) MQ_PRIO_MAX))
return -EINVAL;
- if (ts) {
- expires = timespec64_to_ktime(*ts);
- timeout = &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 char __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, unsigned 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 = NULL;
- struct posix_msg_tree_node *new_leaf = NULL;
if (ts) {
expires = timespec64_to_ktime(*ts);
timeout = &expires;
}
+ return do_mq_sendmsg(mqdes, u_msg_ptr, msg_len, msg_prio, ts, timeout);
+}
+
+static ssize_t do_mq_recvmsg(mqd_t mqdes, char __user *u_msg_ptr, size_t msg_len,
+ unsigned int __user *u_msg_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 = NULL;
+
audit_mq_sendrecv(mqdes, msg_len, 0, ts);
CLASS(fd, f)(mqdes);
@@ -1230,13 +1281,362 @@ 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, size_t msg_len,
+ unsigned int __user *u_msg_prio, struct timespec64 *ts)
+{
+ ktime_t expires, *timeout = NULL;
+
+ if (ts) {
+ expires = timespec64_to_ktime(*ts);
+ timeout = &expires;
+ }
+
+ return do_mq_recvmsg(mqdes, u_msg_ptr, msg_len, u_msg_prio, ts, timeout);
+}
+
+static struct msg_msg *mq_peek_index(struct mqueue_inode_info *info, unsigned 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 = 0;
+
+ node = info->msg_tree.rb_node;
+ while (node) {
+ leaf = rb_entry(node, struct posix_msg_tree_node, rb_node);
+ right_count = get_subtree_count(node->rb_right);
+
+ if (index < right_count) {
+ node = node->rb_right;
+ } else if (index < (right_count + leaf->msg_count)) {
+ /* Target is at this priority level */
+ offset = index - right_count;
+ list_for_each_entry(msg, &leaf->msg_list, m_list) {
+ if (i == offset)
+ return msg;
+ i++;
+ }
+ break;
+ } else {
+ index -= (right_count + leaf->msg_count);
+ node = node->rb_left;
+ }
+ }
+
+ return NULL;
+}
+
+static ssize_t do_mq_recvmsg2(mqd_t mqdes, struct mq_msg_attrs *args, unsigned int flags,
+ unsigned long index, const 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 = file_inode(fd_file(f));
+ if (unlikely(fd_file(f)->f_op != &mqueue_file_operations))
+ return -EBADF;
+
+ info = 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 >= (unsigned long)info->attr.mq_maxmsg)
+ return -EINVAL;
+
+ spin_lock(&info->lock);
+
+ if (info->attr.mq_curmsgs == 0) {
+ spin_unlock(&info->lock);
+ return -EAGAIN;
+ }
+ msg_ptr = mq_peek_index(info, index);
+ if (!msg_ptr) {
+ spin_unlock(&info->lock);
+ return -ENODATA;
+ }
+ k_m_type = msg_ptr->m_type;
+ k_m_ts = msg_ptr->m_ts;
+
+ spin_unlock(&info->lock);
+
+ k_msg_buffer = alloc_msg(k_m_ts);
+
+ if (!k_msg_buffer)
+ return -ENOMEM;
+ ret = security_msg_msg_alloc(k_msg_buffer);
+ if (ret)
+ return ret;
+
+ /*
+ * Two spin locks are necessary here. We are avoiding atomic memory
+ * allocation and premature allocation before confirming
+ * a message actually exists to peek and retrieving required buffer size
+ * when first lock was taken.
+ */
+ spin_lock(&info->lock);
+
+ msg_ptr = mq_peek_index(info, index);
+ if (!msg_ptr || msg_ptr->m_type != k_m_type ||
+ msg_ptr->m_ts != k_m_ts) {
+ spin_unlock(&info->lock);
+ free_msg(k_msg_buffer);
+ return -EAGAIN;
+ }
+ msg_ptr = 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 = 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,
+ const struct compat_mq_mmsg_attrs __user *uattrs,
+ unsigned int attrs_len)
+{
+ struct compat_mq_mmsg_attrs v = {};
+ int err;
+
+ if (unlikely(attrs_len < sizeof(v)))
+ return -EINVAL;
+ if (unlikely(attrs_len > PAGE_SIZE))
+ return -E2BIG;
+ err = copy_struct_from_user(&v, sizeof(v), uattrs, attrs_len);
+
+ if (err)
+ return err;
+ attrs->msg_attrs_vec = (struct iovec __user *)compat_ptr((unsigned long)v.msg_attrs_vec);
+ attrs->ret = (int *)compat_ptr(v.ret);
+ attrs->vlen = 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 = {};
+
+ if (desc_iov->iov_len != sizeof(v))
+ return -EINVAL;
+ if (copy_from_user(&v, desc_iov->iov_base, sizeof(v)))
+ return -EFAULT;
+
+ attr->msg_len = v.msg_len;
+ attr->msg_prio = (unsigned int *)compat_ptr(v.msg_prio);
+ attr->msg_ptr = compat_ptr(v.msg_ptr);
+ return 0;
+ }
+
+#endif
+
+static int mq_mmsg_copy_msg_attr(struct mq_msg_attrs *attr,
+ const struct iovec *desc_iov, bool compat)
+{
+ if (compat)
+ return mq_mmsg_copy_compat_msg_attr(attr, desc_iov);
+ if (desc_iov->iov_len != 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 *attrs,
+ unsigned int flags, unsigned long start_idx,
+ struct timespec64 *ts, bool compat)
+{
+ struct iov_iter iter;
+ struct iovec outer_iovstack[UIO_FASTIOV], *free_iov = outer_iovstack;
+ const struct iovec *msg_iov;
+ ktime_t batch_deadline, *timeout = NULL;
+ ssize_t ret = 0;
+ ssize_t batch_success = 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 = __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 = iter_iov(&iter);
+
+ /* One absolute deadline for the whole batch. */
+ if (ts) {
+ batch_deadline = timespec64_to_ktime(*ts);
+ timeout = &batch_deadline;
+ }
+ for (i = start_idx; i < attrs->vlen; i++) {
+ struct mq_msg_attrs desc = {};
+
+ ret = mq_mmsg_copy_msg_attr(&desc, &msg_iov[i], compat);
+ if (ret < 0)
+ break;
+
+ index = (flags & MQ_PEEK) ? i : 0;
+ ret = do_mq_recvmsg2(mqdes, &desc, flags, index, ts, timeout);
+
+ if (ret < 0) {
+ if (attrs->ret && put_user(ret, attrs->ret)) {
+ kfree(free_iov);
+ return -EFAULT;
+ }
+ break;
+ }
+ batch_success++;
+ }
+
+ if (!(batch_success != 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 *attrs,
+ unsigned long start_idx, struct timespec64 *ts,
+ bool compat)
+{
+ struct iov_iter iter;
+ struct iovec outer_iovstack[UIO_FASTIOV], *free_iov = outer_iovstack;
+ const struct iovec *msg_iov;
+ ktime_t batch_deadline, *timeout = NULL;
+ ssize_t ret = 0;
+ ssize_t batch_success = 0;
+ unsigned int i;
+
+ if (!attrs->vlen || attrs->vlen > UIO_MAXIOV || start_idx > attrs->vlen)
+ return -EINVAL;
+
+ ret = __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 = iter_iov(&iter);
+
+ if (ts) {
+ batch_deadline = timespec64_to_ktime(*ts);
+ timeout = &batch_deadline;
+ }
+ for (i = start_idx; i < attrs->vlen; i++) {
+ struct mq_msg_attrs desc = {};
+ unsigned int msg_prio = 0;
+
+ ret = mq_mmsg_copy_msg_attr(&desc, &msg_iov[i], compat);
+ if (ret < 0)
+ break;
+ if (!desc.msg_prio) {
+ ret = -EINVAL;
+ break;
+ }
+ if (get_user(msg_prio, desc.msg_prio)) {
+ ret = -EFAULT;
+ break;
+ }
+ ret = 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 != 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_ptr,
- 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 = NULL;
+
if (u_abs_timeout) {
int res = prepare_timeout(u_abs_timeout, &ts);
+
if (res)
return res;
p = &ts;
@@ -1244,13 +1644,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 = {};
+ struct timespec64 ts, *p = NULL;
+ int ret;
+
+ ret = mq_mmsg_copy_attrs_from_user(&kattrs, attrs, attrs_len);
+ if (ret)
+ return ret;
+ if (u_abs_timeout) {
+ int res = prepare_timeout(u_abs_timeout, &ts);
+
+ if (res)
+ return res;
+ p = &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 = NULL;
+
if (u_abs_timeout) {
int res = prepare_timeout(u_abs_timeout, &ts);
+
if (res)
return res;
p = &ts;
@@ -1258,6 +1681,26 @@ SYSCALL_DEFINE5(mq_timedreceive, mqd_t, mqdes, char __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 = {};
+ struct timespec64 ts, *p = NULL;
+ int ret;
+
+ ret = mq_mmsg_copy_attrs_from_user(&kattrs, attrs, attrs_len);
+ if (ret)
+ return ret;
+ if (u_abs_timeout) {
+ int res = prepare_timeout(u_abs_timeout, &ts);
+
+ if (res)
+ return res;
+ p = &ts;
+ }
+ return do_mq_recvmmsg(mqdes, &kattrs, flags, start_idx, p, false);
+}
/*
* Notes: the case when user wants us to deregister (with NULL as pointer)
* and he isn't currently owner of notification, will be silently discarded.
@@ -1460,7 +1903,7 @@ struct compat_mq_attr {
};
static inline int get_compat_mq_attr(struct mq_attr *attr,
- const struct compat_mq_attr __user *uattr)
+ const struct compat_mq_attr __user *uattr)
{
struct compat_mq_attr v;
@@ -1476,7 +1919,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)
+ struct compat_mq_attr __user *uattr)
{
struct compat_mq_attr v;
@@ -1541,11 +1984,61 @@ COMPAT_SYSCALL_DEFINE3(mq_getsetattr, mqd_t, mqdes,
return -EFAULT;
return 0;
}
+
+COMPAT_SYSCALL_DEFINE5(mq_sendmmsg, mqd_t, mqdes, struct compat_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 = {};
+ struct timespec64 ts, *p = NULL;
+ struct iovec msg_atrrs_vec = {};
+ int ret;
+
+ kattrs.msg_attrs_vec = &msg_atrrs_vec;
+ ret = mq_mmsg_copy_compat_attrs(&kattrs, attrs, attrs_len);
+ if (ret)
+ return ret;
+
+ if (u_abs_timeout) {
+ int res = prepare_timeout(u_abs_timeout, &ts);
+
+ if (res)
+ return res;
+ p = &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, unsigned int, flags, unsigned long, start_idx,
+ const struct __kernel_timespec __user *, u_abs_timeout)
+{
+ struct mq_mmsg_attrs kattrs = {};
+ struct timespec64 ts, *p = NULL;
+ struct iovec msg_atrrs_vec = {};
+ int ret;
+
+ kattrs.msg_attrs_vec = &msg_atrrs_vec;
+ ret = mq_mmsg_copy_compat_attrs(&kattrs, attrs, attrs_len);
+ if (ret)
+ return ret;
+
+ if (u_abs_timeout) {
+ int res = prepare_timeout(u_abs_timeout, &ts);
+
+ if (res)
+ return res;
+ p = &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 timespec64 *ts)
{
if (get_old_timespec32(ts, p))
return -EFAULT;
@@ -1560,8 +2053,10 @@ SYSCALL_DEFINE5(mq_timedsend_time32, mqd_t, mqdes,
const struct old_timespec32 __user *, u_abs_timeout)
{
struct timespec64 ts, *p = NULL;
+
if (u_abs_timeout) {
int res = compat_prepare_timeout(u_abs_timeout, &ts);
+
if (res)
return res;
p = &ts;
@@ -1575,14 +2070,17 @@ SYSCALL_DEFINE5(mq_timedreceive_time32, mqd_t, mqdes,
const struct old_timespec32 __user *, u_abs_timeout)
{
struct timespec64 ts, *p = NULL;
+
if (u_abs_timeout) {
int res = compat_prepare_timeout(u_abs_timeout, &ts);
+
if (res)
return res;
p = &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 = {
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 = copy_msg(msg, copy);
+ msg = copy_msg(msg, copy, msg->m_ts);
goto out_unlock0;
}
diff --git a/ipc/msgutil.c b/ipc/msgutil.c
index e28f0cecb2ec..f137fb2046a7 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, size_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 = 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 = min(len, DATALEN_MSG);
- memcpy(dst + 1, src + 1, alen);
+ chunk = min(len, DATALEN_MSG);
- for (dst_pseg = dst->next, src_pseg = src->next;
- src_pseg != NULL;
- dst_pseg = dst_pseg->next, src_pseg = src_pseg->next) {
+ memcpy(dst + 1, src + 1, chunk);
+ remaining = len - chunk;
+ src_seg = src->next;
+ dst_seg = dst->next;
- len -= alen;
- alen = min(len, DATALEN_SEG);
- memcpy(dst_pseg + 1, src_pseg + 1, alen);
+ while (remaining > 0 && src_seg && dst_seg) {
+ chunk = min(remaining, DATALEN_SEG);
+ memcpy(dst_seg + 1, src_seg + 1, chunk);
+ remaining -= chunk;
+ src_seg = src_seg->next;
+ dst_seg = dst_seg->next;
}
-
+ if (remaining != 0)
+ return ERR_PTR(-EINVAL);
dst->m_type = src->m_type;
dst->m_ts = src->m_ts;
-
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
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v3 2/4] IPC: Added system call entry in all of most common architectures
2026-06-20 11:23 [PATCH v3 0/4] Add two new system call mq_recvmmsg() and mq_sendmmsg() to posix ipc mqueue Mathura_Kumar
2026-06-20 11:23 ` [PATCH v3 1/4] IPC: Added two new system call mq_recvmmsg() and mq_sendmmsg() Mathura_Kumar
@ 2026-06-20 11:23 ` Mathura_Kumar
2026-06-20 11:23 ` [PATCH v3 3/4] IPC: Added system call entry in performance tool Mathura_Kumar
2026-06-20 11:23 ` [PATCH v3 4/4] Test: Added self-testing and documentation Mathura_Kumar
3 siblings, 0 replies; 12+ messages in thread
From: Mathura_Kumar @ 2026-06-20 11:23 UTC (permalink / raw)
To: criu; +Cc: academic1mathura, avagin, ptikhomirov
Wire up the new syscall numbers for all common architectures
in their respective syscall tables and unistd headers.
Signed-off-by: Mathura_Kumar <academic1mathura@gmail.com>
---
arch/alpha/kernel/syscalls/syscall.tbl | 2 ++
arch/arm/tools/syscall.tbl | 2 ++
arch/arm64/tools/syscall_32.tbl | 3 +++
arch/m68k/kernel/syscalls/syscall.tbl | 2 ++
arch/microblaze/kernel/syscalls/syscall.tbl | 2 ++
arch/mips/kernel/syscalls/syscall_n32.tbl | 2 ++
arch/mips/kernel/syscalls/syscall_n64.tbl | 3 +++
arch/mips/kernel/syscalls/syscall_o32.tbl | 2 ++
arch/parisc/kernel/syscalls/syscall.tbl | 2 ++
arch/powerpc/kernel/syscalls/syscall.tbl | 2 ++
arch/s390/kernel/syscalls/syscall.tbl | 3 +++
arch/sh/kernel/syscalls/syscall.tbl | 2 ++
arch/sparc/kernel/syscalls/syscall.tbl | 3 +++
arch/x86/entry/syscalls/syscall_32.tbl | 2 ++
arch/x86/entry/syscalls/syscall_64.tbl | 3 ++-
arch/xtensa/kernel/syscalls/syscall.tbl | 2 ++
scripts/syscall.tbl | 2 ++
17 files changed, 38 insertions(+), 1 deletion(-)
diff --git a/arch/alpha/kernel/syscalls/syscall.tbl b/arch/alpha/kernel/syscalls/syscall.tbl
index f31b7afffc34..918e38415665 100644
--- a/arch/alpha/kernel/syscalls/syscall.tbl
+++ b/arch/alpha/kernel/syscalls/syscall.tbl
@@ -511,3 +511,5 @@
579 common file_setattr sys_file_setattr
580 common listns sys_listns
581 common rseq_slice_yield sys_rseq_slice_yield
+582 common mq_recvmmsg sys_mq_recvmmsg
+583 common mq_sendmmsg sys_mq_sendmmsg
diff --git a/arch/arm/tools/syscall.tbl b/arch/arm/tools/syscall.tbl
index 94351e22bfcf..cc44a0a56bf2 100644
--- a/arch/arm/tools/syscall.tbl
+++ b/arch/arm/tools/syscall.tbl
@@ -486,3 +486,5 @@
469 common file_setattr sys_file_setattr
470 common listns sys_listns
471 common rseq_slice_yield sys_rseq_slice_yield
+472 common mq_recvmmsg sys_mq_recvmmsg
+473 common mq_sendmmsg sys_mq_sendmmsg
diff --git a/arch/arm64/tools/syscall_32.tbl b/arch/arm64/tools/syscall_32.tbl
index 62d93d88e0fe..3b50eda2970c 100644
--- a/arch/arm64/tools/syscall_32.tbl
+++ b/arch/arm64/tools/syscall_32.tbl
@@ -483,3 +483,6 @@
469 common file_setattr sys_file_setattr
470 common listns sys_listns
471 common rseq_slice_yield sys_rseq_slice_yield
+472 common mq_recvmmsg sys_mq_recvmmsg compat_sys_mq_recvmmsg
+473 common mq_sendmmsg sys_mq_sendmmsg compat_sys_mq_sendmmsg
+
diff --git a/arch/m68k/kernel/syscalls/syscall.tbl b/arch/m68k/kernel/syscalls/syscall.tbl
index 248934257101..37f81ca45aee 100644
--- a/arch/m68k/kernel/syscalls/syscall.tbl
+++ b/arch/m68k/kernel/syscalls/syscall.tbl
@@ -471,3 +471,5 @@
469 common file_setattr sys_file_setattr
470 common listns sys_listns
471 common rseq_slice_yield sys_rseq_slice_yield
+472 common mq_recvmmsg sys_mq_recvmmsg
+473 common mq_sendmmsg sys_mq_sendmmsg
diff --git a/arch/microblaze/kernel/syscalls/syscall.tbl b/arch/microblaze/kernel/syscalls/syscall.tbl
index 223d26303627..7a42b1ac167d 100644
--- a/arch/microblaze/kernel/syscalls/syscall.tbl
+++ b/arch/microblaze/kernel/syscalls/syscall.tbl
@@ -477,3 +477,5 @@
469 common file_setattr sys_file_setattr
470 common listns sys_listns
471 common rseq_slice_yield sys_rseq_slice_yield
+473 common mq_sendmmsg sys_mq_sendmmsg
+472 common mq_recvmmsg sys_mq_recvmmsg
diff --git a/arch/mips/kernel/syscalls/syscall_n32.tbl b/arch/mips/kernel/syscalls/syscall_n32.tbl
index 7430714e2b8f..60e67d7e2b0e 100644
--- a/arch/mips/kernel/syscalls/syscall_n32.tbl
+++ b/arch/mips/kernel/syscalls/syscall_n32.tbl
@@ -410,3 +410,5 @@
469 n32 file_setattr sys_file_setattr
470 n32 listns sys_listns
471 n32 rseq_slice_yield sys_rseq_slice_yield
+472 n32 mq_recvmmsg compat_sys_mq_recvmmsg
+473 n32 mq_sendmmsg compat_sys_mq_sendmmsg
diff --git a/arch/mips/kernel/syscalls/syscall_n64.tbl b/arch/mips/kernel/syscalls/syscall_n64.tbl
index 630aab9e5425..49b52129b110 100644
--- a/arch/mips/kernel/syscalls/syscall_n64.tbl
+++ b/arch/mips/kernel/syscalls/syscall_n64.tbl
@@ -386,3 +386,6 @@
469 n64 file_setattr sys_file_setattr
470 n64 listns sys_listns
471 n64 rseq_slice_yield sys_rseq_slice_yield
+472 n64 mq_recvmmsg sys_mq_recvmmsg
+473 n64 mq_sendmmsg sys_mq_sendmmsg
+
diff --git a/arch/mips/kernel/syscalls/syscall_o32.tbl b/arch/mips/kernel/syscalls/syscall_o32.tbl
index 128653112284..0cc9e5ebc7c9 100644
--- a/arch/mips/kernel/syscalls/syscall_o32.tbl
+++ b/arch/mips/kernel/syscalls/syscall_o32.tbl
@@ -459,3 +459,5 @@
469 o32 file_setattr sys_file_setattr
470 o32 listns sys_listns
471 o32 rseq_slice_yield sys_rseq_slice_yield
+472 o32 mq_recvmmsg sys_mq_recvmmsg compat_sys_mq_recvmmsg
+473 o32 mq_sendmmsg sys_mq_sendmmsg compat_sys_mq_sendmmsg
diff --git a/arch/parisc/kernel/syscalls/syscall.tbl b/arch/parisc/kernel/syscalls/syscall.tbl
index c6331dad9461..de3406745995 100644
--- a/arch/parisc/kernel/syscalls/syscall.tbl
+++ b/arch/parisc/kernel/syscalls/syscall.tbl
@@ -470,3 +470,5 @@
469 common file_setattr sys_file_setattr
470 common listns sys_listns
471 common rseq_slice_yield sys_rseq_slice_yield
+472 common mq_recvmmsg sys_mq_recvmmsg compat_sys_mq_recvmmsg
+473 common mq_sendmmsg sys_mq_sendmmsg compat_sys_mq_sendmmsg
diff --git a/arch/powerpc/kernel/syscalls/syscall.tbl b/arch/powerpc/kernel/syscalls/syscall.tbl
index 4fcc7c58a105..820a77c6b93b 100644
--- a/arch/powerpc/kernel/syscalls/syscall.tbl
+++ b/arch/powerpc/kernel/syscalls/syscall.tbl
@@ -562,3 +562,5 @@
469 common file_setattr sys_file_setattr
470 common listns sys_listns
471 nospu rseq_slice_yield sys_rseq_slice_yield
+472 common mq_recvmmsg sys_mq_recvmmsg compat_sys_mq_recvmmsg
+473 common mq_sendmmsg sys_mq_sendmmsg compat_sys_mq_sendmmsg
diff --git a/arch/s390/kernel/syscalls/syscall.tbl b/arch/s390/kernel/syscalls/syscall.tbl
index 09a7ef04d979..acf0eabbd629 100644
--- a/arch/s390/kernel/syscalls/syscall.tbl
+++ b/arch/s390/kernel/syscalls/syscall.tbl
@@ -398,3 +398,6 @@
469 common file_setattr sys_file_setattr
470 common listns sys_listns
471 common rseq_slice_yield sys_rseq_slice_yield
+472 common mq_recvmmsg sys_mq_recvmmsg
+473 common mq_sendmmsg sys_mq_sendmmsg
+
diff --git a/arch/sh/kernel/syscalls/syscall.tbl b/arch/sh/kernel/syscalls/syscall.tbl
index 70b315cbe710..f572b846a0d1 100644
--- a/arch/sh/kernel/syscalls/syscall.tbl
+++ b/arch/sh/kernel/syscalls/syscall.tbl
@@ -475,3 +475,5 @@
469 common file_setattr sys_file_setattr
470 common listns sys_listns
471 common rseq_slice_yield sys_rseq_slice_yield
+472 common mq_recvmmsg sys_mq_recvmmsg
+473 common mq_sendmmsg sys_mq_sendmmsg
diff --git a/arch/sparc/kernel/syscalls/syscall.tbl b/arch/sparc/kernel/syscalls/syscall.tbl
index 7e71bf7fcd14..a0e588531a4e 100644
--- a/arch/sparc/kernel/syscalls/syscall.tbl
+++ b/arch/sparc/kernel/syscalls/syscall.tbl
@@ -517,3 +517,6 @@
469 common file_setattr sys_file_setattr
470 common listns sys_listns
471 common rseq_slice_yield sys_rseq_slice_yield
+472 common mq_recvmmsg sys_mq_recvmmsg compat_sys_mq_recvmmsg
+473 common mq_sendmmsg sys_mq_sendmmsg compat_sys_mq_sendmmsg
+
diff --git a/arch/x86/entry/syscalls/syscall_32.tbl b/arch/x86/entry/syscalls/syscall_32.tbl
index f832ebd2d79b..b40cce591f20 100644
--- a/arch/x86/entry/syscalls/syscall_32.tbl
+++ b/arch/x86/entry/syscalls/syscall_32.tbl
@@ -477,3 +477,5 @@
469 i386 file_setattr sys_file_setattr
470 i386 listns sys_listns
471 i386 rseq_slice_yield sys_rseq_slice_yield
+472 i386 mq_recvmmsg sys_mq_recvmmsg compat_sys_mq_recvmmsg
+473 i386 mq_sendmmsg sys_mq_sendmmsg compat_sys_mq_sendmmsg
diff --git a/arch/x86/entry/syscalls/syscall_64.tbl b/arch/x86/entry/syscalls/syscall_64.tbl
index 524155d655da..16b29007e210 100644
--- a/arch/x86/entry/syscalls/syscall_64.tbl
+++ b/arch/x86/entry/syscalls/syscall_64.tbl
@@ -396,7 +396,8 @@
469 common file_setattr sys_file_setattr
470 common listns sys_listns
471 common rseq_slice_yield sys_rseq_slice_yield
-
+472 common mq_recvmmsg sys_mq_recvmmsg
+473 common mq_sendmmsg sys_mq_sendmmsg
#
# Due to a historical design error, certain syscalls are numbered differently
# in x32 as compared to native x86_64. These syscalls have numbers 512-547.
diff --git a/arch/xtensa/kernel/syscalls/syscall.tbl b/arch/xtensa/kernel/syscalls/syscall.tbl
index a9bca4e484de..02f8f1da88b5 100644
--- a/arch/xtensa/kernel/syscalls/syscall.tbl
+++ b/arch/xtensa/kernel/syscalls/syscall.tbl
@@ -442,3 +442,5 @@
469 common file_setattr sys_file_setattr
470 common listns sys_listns
471 common rseq_slice_yield sys_rseq_slice_yield
+472 common mq_recvmmsg sys_mq_recvmmsg
+473 common mq_sendmmsg sys_mq_sendmmsg
diff --git a/scripts/syscall.tbl b/scripts/syscall.tbl
index 7a42b32b6577..a3a2345732bb 100644
--- a/scripts/syscall.tbl
+++ b/scripts/syscall.tbl
@@ -412,3 +412,5 @@
469 common file_setattr sys_file_setattr
470 common listns sys_listns
471 common rseq_slice_yield sys_rseq_slice_yield
+472 common mq_recvmmsg sys_mq_recvmmsg compat_sys_mq_recvmmsg
+473 common mq_sendmmsg sys_mq_sendmmsg compat_sys_mq_sendmmsg
--
2.43.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v3 3/4] IPC: Added system call entry in performance tool
2026-06-20 11:23 [PATCH v3 0/4] Add two new system call mq_recvmmsg() and mq_sendmmsg() to posix ipc mqueue Mathura_Kumar
2026-06-20 11:23 ` [PATCH v3 1/4] IPC: Added two new system call mq_recvmmsg() and mq_sendmmsg() Mathura_Kumar
2026-06-20 11:23 ` [PATCH v3 2/4] IPC: Added system call entry in all of most common architectures Mathura_Kumar
@ 2026-06-20 11:23 ` Mathura_Kumar
2026-06-20 11:23 ` [PATCH v3 4/4] Test: Added self-testing and documentation Mathura_Kumar
3 siblings, 0 replies; 12+ messages in thread
From: Mathura_Kumar @ 2026-06-20 11:23 UTC (permalink / raw)
To: criu; +Cc: academic1mathura, avagin, ptikhomirov
It include all the entires made up in performance tools
for both new system call.
Signed-off-by: Mathura_Kumar <academic1mathura@gmail.com>
---
tools/include/uapi/asm-generic/unistd.h | 8 +++++++-
tools/perf/arch/alpha/entry/syscalls/syscall.tbl | 4 ++++
tools/perf/arch/arm/entry/syscalls/syscall.tbl | 3 +++
tools/perf/arch/arm64/entry/syscalls/syscall_32.tbl | 3 +++
tools/perf/arch/mips/entry/syscalls/syscall_n64.tbl | 2 ++
tools/perf/arch/parisc/entry/syscalls/syscall.tbl | 3 +++
tools/perf/arch/powerpc/entry/syscalls/syscall.tbl | 3 +++
tools/perf/arch/s390/entry/syscalls/syscall.tbl | 2 ++
tools/perf/arch/sh/entry/syscalls/syscall.tbl | 2 ++
tools/perf/arch/sparc/entry/syscalls/syscall.tbl | 2 ++
tools/perf/arch/x86/entry/syscalls/syscall_32.tbl | 2 ++
tools/perf/arch/x86/entry/syscalls/syscall_64.tbl | 2 ++
tools/perf/arch/xtensa/entry/syscalls/syscall.tbl | 2 ++
tools/scripts/syscall.tbl | 2 ++
14 files changed, 39 insertions(+), 1 deletion(-)
diff --git a/tools/include/uapi/asm-generic/unistd.h b/tools/include/uapi/asm-generic/unistd.h
index a627acc8fb5f..b891edfebdf9 100644
--- a/tools/include/uapi/asm-generic/unistd.h
+++ b/tools/include/uapi/asm-generic/unistd.h
@@ -863,8 +863,14 @@ __SYSCALL(__NR_listns, sys_listns)
#define __NR_rseq_slice_yield 471
__SYSCALL(__NR_rseq_slice_yield, sys_rseq_slice_yield)
+#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 472
+#define __NR_syscalls 474
/*
* 32 bit systems traditionally used different
diff --git a/tools/perf/arch/alpha/entry/syscalls/syscall.tbl b/tools/perf/arch/alpha/entry/syscalls/syscall.tbl
index 74720667fe09..5cf0c5d3f123 100644
--- a/tools/perf/arch/alpha/entry/syscalls/syscall.tbl
+++ b/tools/perf/arch/alpha/entry/syscalls/syscall.tbl
@@ -502,3 +502,7 @@
570 common lsm_set_self_attr sys_lsm_set_self_attr
571 common lsm_list_modules sys_lsm_list_modules
572 common mseal sys_mseal
+# 573-581 are kept reserved for existing system call
+582 common mq_recvmmsg sys_mq_recvmmsg
+583 common mq_sendmmsg sys_mq_sendmmsg
+
diff --git a/tools/perf/arch/arm/entry/syscalls/syscall.tbl b/tools/perf/arch/arm/entry/syscalls/syscall.tbl
index 94351e22bfcf..868bbb6539a5 100644
--- a/tools/perf/arch/arm/entry/syscalls/syscall.tbl
+++ b/tools/perf/arch/arm/entry/syscalls/syscall.tbl
@@ -486,3 +486,6 @@
469 common file_setattr sys_file_setattr
470 common listns sys_listns
471 common rseq_slice_yield sys_rseq_slice_yield
+472 common mq_recvmmsg sys_mq_recvmmsg
+473 common mq_sendmmsg sys_mq_sendmmsg
+
diff --git a/tools/perf/arch/arm64/entry/syscalls/syscall_32.tbl b/tools/perf/arch/arm64/entry/syscalls/syscall_32.tbl
index 9a37930d4e26..a6103dbcd359 100644
--- a/tools/perf/arch/arm64/entry/syscalls/syscall_32.tbl
+++ b/tools/perf/arch/arm64/entry/syscalls/syscall_32.tbl
@@ -474,3 +474,6 @@
460 common lsm_set_self_attr sys_lsm_set_self_attr
461 common lsm_list_modules sys_lsm_list_modules
462 common mseal sys_mseal
+# 463-471 are kept reserved for existing system call
+472 common mq_recvmmsg sys_mq_recvmmsg compat_sys_mq_recvmmsg
+473 common mq_sendmmsg sys_mq_sendmmsg compat_sys_mq_sendmmsg
diff --git a/tools/perf/arch/mips/entry/syscalls/syscall_n64.tbl b/tools/perf/arch/mips/entry/syscalls/syscall_n64.tbl
index 630aab9e5425..51d0a1d5023f 100644
--- a/tools/perf/arch/mips/entry/syscalls/syscall_n64.tbl
+++ b/tools/perf/arch/mips/entry/syscalls/syscall_n64.tbl
@@ -386,3 +386,5 @@
469 n64 file_setattr sys_file_setattr
470 n64 listns sys_listns
471 n64 rseq_slice_yield sys_rseq_slice_yield
+472 n64 mq_recvmmsg sys_mq_recvmmsg
+473 n64 mq_sendmmsg sys_mq_sendmmsg
diff --git a/tools/perf/arch/parisc/entry/syscalls/syscall.tbl b/tools/perf/arch/parisc/entry/syscalls/syscall.tbl
index 66dc406b12e4..a93b7b0d6ac1 100644
--- a/tools/perf/arch/parisc/entry/syscalls/syscall.tbl
+++ b/tools/perf/arch/parisc/entry/syscalls/syscall.tbl
@@ -461,3 +461,6 @@
460 common lsm_set_self_attr sys_lsm_set_self_attr
461 common lsm_list_modules sys_lsm_list_modules
462 common mseal sys_mseal
+472 common mq_recvmmsg sys_mq_recvmmsg compat_sys_mq_recvmmsg
+473 common mq_sendmmsg sys_mq_sendmmsg compat_sys_mq_sendmmsg
+
diff --git a/tools/perf/arch/powerpc/entry/syscalls/syscall.tbl b/tools/perf/arch/powerpc/entry/syscalls/syscall.tbl
index 4fcc7c58a105..a6c97ec03646 100644
--- a/tools/perf/arch/powerpc/entry/syscalls/syscall.tbl
+++ b/tools/perf/arch/powerpc/entry/syscalls/syscall.tbl
@@ -562,3 +562,6 @@
469 common file_setattr sys_file_setattr
470 common listns sys_listns
471 nospu rseq_slice_yield sys_rseq_slice_yield
+472 common mq_recvmmsg sys_mq_recvmmsg compat_sys_mq_recvmmsg
+473 common mq_sendmmsg sys_mq_sendmmsg compat_sys_mq_sendmmsg
+
diff --git a/tools/perf/arch/s390/entry/syscalls/syscall.tbl b/tools/perf/arch/s390/entry/syscalls/syscall.tbl
index 09a7ef04d979..c9f9b89c24f0 100644
--- a/tools/perf/arch/s390/entry/syscalls/syscall.tbl
+++ b/tools/perf/arch/s390/entry/syscalls/syscall.tbl
@@ -398,3 +398,5 @@
469 common file_setattr sys_file_setattr
470 common listns sys_listns
471 common rseq_slice_yield sys_rseq_slice_yield
+472 common mq_recvmmsg sys_mq_recvmmsg
+473 common mq_sendmmsg sys_mq_sendmmsg
diff --git a/tools/perf/arch/sh/entry/syscalls/syscall.tbl b/tools/perf/arch/sh/entry/syscalls/syscall.tbl
index 70b315cbe710..2888f649ba20 100644
--- a/tools/perf/arch/sh/entry/syscalls/syscall.tbl
+++ b/tools/perf/arch/sh/entry/syscalls/syscall.tbl
@@ -475,3 +475,5 @@
469 common file_setattr sys_file_setattr
470 common listns sys_listns
471 common rseq_slice_yield sys_rseq_slice_yield
+472 common mq_recvmmsg sys_mq_recvmmsg
+473 common mq_sendmmsg sys_mq_sendmmsg
diff --git a/tools/perf/arch/sparc/entry/syscalls/syscall.tbl b/tools/perf/arch/sparc/entry/syscalls/syscall.tbl
index 7e71bf7fcd14..7f52018977c6 100644
--- a/tools/perf/arch/sparc/entry/syscalls/syscall.tbl
+++ b/tools/perf/arch/sparc/entry/syscalls/syscall.tbl
@@ -517,3 +517,5 @@
469 common file_setattr sys_file_setattr
470 common listns sys_listns
471 common rseq_slice_yield sys_rseq_slice_yield
+472 common mq_recvmmsg sys_mq_recvmmsg compat_sys_mq_recvmmsg
+473 common mq_sendmmsg sys_mq_sendmmsg compat_sys_mq_sendmmsg
diff --git a/tools/perf/arch/x86/entry/syscalls/syscall_32.tbl b/tools/perf/arch/x86/entry/syscalls/syscall_32.tbl
index f832ebd2d79b..85a847c2a784 100644
--- a/tools/perf/arch/x86/entry/syscalls/syscall_32.tbl
+++ b/tools/perf/arch/x86/entry/syscalls/syscall_32.tbl
@@ -477,3 +477,5 @@
469 i386 file_setattr sys_file_setattr
470 i386 listns sys_listns
471 i386 rseq_slice_yield sys_rseq_slice_yield
+472 i386 mq_recvmmsg sys_mq_recvmmsg compat_sys_mq_recvmmsg
+473 i386 mq_sendmmsg sys_mq_sendmmsg compat_sys_mq_sendmmsg
diff --git a/tools/perf/arch/x86/entry/syscalls/syscall_64.tbl b/tools/perf/arch/x86/entry/syscalls/syscall_64.tbl
index 524155d655da..b4f065b311b4 100644
--- a/tools/perf/arch/x86/entry/syscalls/syscall_64.tbl
+++ b/tools/perf/arch/x86/entry/syscalls/syscall_64.tbl
@@ -396,6 +396,8 @@
469 common file_setattr sys_file_setattr
470 common listns sys_listns
471 common rseq_slice_yield sys_rseq_slice_yield
+472 common mq_recvmmsg sys_mq_recvmmsg
+473 common mq_sendmmsg sys_mq_sendmmsg
#
# Due to a historical design error, certain syscalls are numbered differently
diff --git a/tools/perf/arch/xtensa/entry/syscalls/syscall.tbl b/tools/perf/arch/xtensa/entry/syscalls/syscall.tbl
index a9bca4e484de..af161058fbb9 100644
--- a/tools/perf/arch/xtensa/entry/syscalls/syscall.tbl
+++ b/tools/perf/arch/xtensa/entry/syscalls/syscall.tbl
@@ -442,3 +442,5 @@
469 common file_setattr sys_file_setattr
470 common listns sys_listns
471 common rseq_slice_yield sys_rseq_slice_yield
+472 common mq_recvmmsg sys_mq_recvmmsg
+473 common mq_sendmmsg sys_mq_sendmmsg
diff --git a/tools/scripts/syscall.tbl b/tools/scripts/syscall.tbl
index 7a42b32b6577..fdfee8bb36a3 100644
--- a/tools/scripts/syscall.tbl
+++ b/tools/scripts/syscall.tbl
@@ -412,3 +412,5 @@
469 common file_setattr sys_file_setattr
470 common listns sys_listns
471 common rseq_slice_yield sys_rseq_slice_yield
+472 common mq_recvmmsg sys_mq_recvmmsg compat_sys_mq_recvmmsg
+473 common mq_sendmmsg sys_mq_sendmmsg compat_sys_mq_sendmmsg
--
2.43.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v3 4/4] Test: Added self-testing and documentation
2026-06-20 11:23 [PATCH v3 0/4] Add two new system call mq_recvmmsg() and mq_sendmmsg() to posix ipc mqueue Mathura_Kumar
` (2 preceding siblings ...)
2026-06-20 11:23 ` [PATCH v3 3/4] IPC: Added system call entry in performance tool Mathura_Kumar
@ 2026-06-20 11:23 ` Mathura_Kumar
3 siblings, 0 replies; 12+ messages in thread
From: Mathura_Kumar @ 2026-06-20 11:23 UTC (permalink / raw)
To: criu; +Cc: academic1mathura, avagin, ptikhomirov
Add test coverage to verify system call behavior under
different-different cases.
Update index documentation to include two new doc.
1) mq_recvmmsg.rst
2) mq_sendmmsg.rst
The tests validate:
1) Offset boundries.
2) Partial success behaviors.
3) Timeout behavior.
Documentation changes include:
1) Updated implementation details.
2) Error handlings mechanism.
3) Tests details.
Signed-off-by: Mathura_Kumar <academic1mathura@gmail.com>
---
Documentation/userspace-api/index.rst | 2 +
Documentation/userspace-api/mq_recvmmsg.rst | 205 ++++++++++
Documentation/userspace-api/mq_sendmmsg.rst | 168 +++++++++
ipc/msgutil.c | 3 +-
tools/testing/selftests/ipc/Makefile | 2 +-
tools/testing/selftests/ipc/mq_recvmmsg.c | 391 ++++++++++++++++++++
tools/testing/selftests/ipc/mq_sendmmsg.c | 360 ++++++++++++++++++
7 files changed, 1128 insertions(+), 3 deletions(-)
create mode 100644 Documentation/userspace-api/mq_recvmmsg.rst
create mode 100644 Documentation/userspace-api/mq_sendmmsg.rst
create mode 100644 tools/testing/selftests/ipc/mq_recvmmsg.c
create mode 100644 tools/testing/selftests/ipc/mq_sendmmsg.c
diff --git a/Documentation/userspace-api/index.rst b/Documentation/userspace-api/index.rst
index a68b1bea57a8..4e9cbdd8c3a8 100644
--- a/Documentation/userspace-api/index.rst
+++ b/Documentation/userspace-api/index.rst
@@ -22,6 +22,8 @@ System calls
ioctl/index
mseal
rseq
+ mq_recvmmsg
+ mq_sendmmsg
Security-related interfaces
===========================
diff --git a/Documentation/userspace-api/mq_recvmmsg.rst b/Documentation/userspace-api/mq_recvmmsg.rst
new file mode 100644
index 000000000000..878e46ed38af
--- /dev/null
+++ b/Documentation/userspace-api/mq_recvmmsg.rst
@@ -0,0 +1,205 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+mq_recvmmsg system call
+=============================
+
+This document describes the mq_recvmmsg() system call. It provides
+an overview of the feature, interface specification, design, and
+test specification.
+
+Contents
+--------
+
+ 1) Overview
+ 2) Functional Specification
+ 3) Design
+ 4) Test Specification
+
+1) Overview
+-----------
+
+POSIX message queues on Linux provide mq_timedreceive() for consuming
+messages from a queue.This interface requires the caller to pass the
+message buffer, length and priority pointer as individual arguments to
+the system call. This imposes a fixed calling convention that cannot be
+extended without breaking the ABI.
+
+mq_recvmmsg() introduces a new system call entry point that accepts
+message buffer parameters via a struct argument inside a iovec rather
+than as individual syscall arguments.
+
+One 64-bit variant is provided with compat handling:
+ mq_recvmmsg()
+
+2) Functional Specification
+---------------------------
+
+NAME
+ mq_recvmmsg - receive or peek as a batch of messages from a
+ POSIX message queue
+
+SYNOPSIS
+
+.. code-block:: c
+
+ #include <mqueue.h>
+
+ 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 *ret;
+ };
+
+ ssize_t 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 *abs_timeout);
+
+
+DESCRIPTION
+ mq_recvmmsg() receives or peeks as a batch of messages from the
+ message queue referred to by the descriptor mqdes.
+
+ The flags argument controls receive behavior. The following
+ flag is defined:
+
+ ``MQ_PEEK``
+ Copies the message into struct mq_msg_attrs without removing it from
+ the queue. The queue is not modified.
+
+ ``MQ_RECV``
+ Copies and consumes messages from the queue up to the count specified
+ by vlen.
+
+ The start_idx argument specifies which message to begin operating on within
+ the priority-ordered queue, relative to the requested batch range.
+
+ The abs_timeout argument specifies an absolute timeout for the entire batch
+ operation. When MQ_PEEK is set, abs_timeout is ignored, since peek is a
+ non-blocking snapshot operation. When MQ_PEEK is not set, abs_timeout be the
+ timeout for the entire batch.
+
+RETURN VALUE
+ On success, returns the number of messages copied into user-space.
+
+ On failure, the function returns an error directly without writing
+ to the ret field of struct mq_mmsg_attrs in two situations:
+
+ * An error occurred before a single message was successfully copied to user space.
+ * new error occurred in writing error itself in ret field of struct mq_mmsg_attrs
+ after partial success before completing batch.
+
+
+ERRORS
+ ``EAGAIN``
+ Queue is empty and MQ_PEEK is set. Peek is always
+ non-blocking and returns immediately on empty queue.
+
+ ``EBADF``
+ mqdes is not a valid message queue descriptor open
+ for reading.
+
+ ``EFAULT``
+ attrs, msg_ptr, msg_prio, or abs_timeout points to
+ an invalid address or any invalid address.
+
+ ``EINVAL``
+ The batch size is too large, start_idx is inconsistent with the
+ current queue state, flags contains an unrecognized value, or contradictory
+ flags were passed.
+
+ ``EMSGSIZE``
+ msg_len is less than the mq_msgsize attribute of
+ the queue.
+
+ ``E2BIG``
+ attrs_len is greater than PAGE_SIZE.
+
+ ``ETIMEDOUT``
+ Pop path only. The call timed out before a message
+ became available. Never returned on peek path.
+
+ ``ENODATA``
+ No valid message exists matching the given parameters.
+
+ ``ENOMEM``
+ Insufficient memory to complete the operation.
+
+3) Design
+---------
+
+3.1 Struct-based argument passing
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The message buffer parameters (msg_ptr, msg_len, msg_prio) are consolidated
+into struct mq_msg_attrs rather than passed as individual syscall arguments.
+These are then referenced via struct mq_mmsg_attrs, which contains an
+iovec msg_attrs_vec and a vlen count. The base of msg_attrs_vec holds individual
+mq_msg_attrs entries, and length store struct mq_msg_attrs size.
+
+
+3.2 Compat handling
+~~~~~~~~~~~~~~~~~~~~
+
+.. code-block:: c
+
+ struct compat_msg_attrs {
+ compat_size_t msg_len;
+ compat_uptr_t msg_prio;
+ compat_uptr_t msg_ptr;
+ };
+
+ struct compat_mq_mmsg_attrs {
+ compat_uptr_t msg_attrs_vec;
+ compat_size_t vlen;
+ compat_uptr_t ret;
+ };
+
+
+The compat entry point performs the necessary conversions before
+calling the shared do_mq_recvmmsg() implementation.
+
+3.3 Peek implementation
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+When MQ_PEEK is set, the implementation locates the target message
+in the priority tree but does not remove it. Two locks are taken:
+the first confirms a message exists before any allocation is
+attempted and retrieve required buffer size to do memory allocation
+outside of critical section, avoiding allocation on empty queues.
+The second protects the kernel temporary buffer copy operation.
+
+3.4 start_idx argument
+~~~~~~~~~~~~~~~~~~~
+
+The start_idx argument specifies which message to begin operating on within the queue.
+If a batch request fails partway through, user space is expected to supply an updated
+start_idx reflecting the number of messages already handled, failing to do so may result
+in unexpected message delivery.
+
+4) Test Specification
+---------------------
+
+Tests for mq_recvmmsg() should cover the following:
+
+1) Basic receive and peek — Verify that without MQ_PEEK the message is consumed and
+the queue depth decreases by one. Verify that the message body, priority, and return
+value are correct.
+
+2) Offset and boundary handling — Verify that the implementation correctly respects
+batch size and start_idx boundary conditions.
+
+3) Partial success — Verify that when an error occurs mid-batch, the call returns
+successfully with the count of messages processed so far, rather than discarding partial
+results.
+
+
+
+
+
diff --git a/Documentation/userspace-api/mq_sendmmsg.rst b/Documentation/userspace-api/mq_sendmmsg.rst
new file mode 100644
index 000000000000..fd2eb0e44514
--- /dev/null
+++ b/Documentation/userspace-api/mq_sendmmsg.rst
@@ -0,0 +1,168 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+mq_sendmmsg system call
+=============================
+
+This document describes the mq_sendmmsg() system call. It provides
+an overview of the feature, interface specification, design, and
+test specification.
+
+Contents
+--------
+
+ 1) Overview
+ 2) Functional Specification
+ 3) Design
+ 4) Test Specification
+
+1) Overview
+-----------
+
+POSIX message queues on Linux provide mq_timedsend() for storing
+messages in a queue one at time.
+
+mq_sendmmsg() introduces a new system call entry point that take
+messages in batch and store in queues.
+
+One 64-bit variant is provided with compat handling:
+ mq_sendmmsg()
+
+2) Functional Specification
+---------------------------
+
+NAME
+ mq_sendmmsg - send messages as a batch to queues.
+
+SYNOPSIS
+
+.. code-block:: c
+
+ #include <mqueue.h>
+
+ 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 *ret;
+ };
+
+ ssize_t mq_sendmmsg(mqd_t mqdes, struct mq_mmsg_attrs *attrs,
+ unsigned int attrs_len, unsigned long start_idx,
+ const struct __kernel_timespec *abs_timeout);
+
+
+DESCRIPTION
+ mq_sendmmsg() send messages in batch to message queue referred
+ to by the descriptor mqdes.
+
+ The start_idx argument specifies which message to begin operating on within
+ relative to the requested batch range.
+
+ The abs_timeout argument specifies an absolute timeout for the entire batch
+ operation.
+
+RETURN VALUE
+ On success, returns the number of messages stored into the queues.
+
+ On failure, the function returns an error directly without writing
+ to the ret field of struct mq_mmsg_attrs in two situations:
+
+ * An error occurred before a single message was successfully stored into the queues.
+ * A new error occurred in writing error itself in ret field of struct mq_mmsg_attrs
+ after partial success before completing batch.
+
+
+ERRORS
+ ``EAGAIN``
+ Queue is not empty.
+
+ ``EBADF``
+ mqdes is not a valid message queue descriptor open
+ for reading.
+
+ ``EFAULT``
+ attrs, msg_ptr, msg_prio, or abs_timeout points to
+ an invalid address.
+
+ ``EINVAL``
+ The batch size is too large, start_idx is inconsistent with the
+ current queue state.
+
+ ``EMSGSIZE``
+ msg_len is greater than the mq_msgsize attribute of
+ the queue.
+
+ ``E2BIG``
+ attrs_len is greater than PAGE_SIZE.
+
+ ``ETIMEDOUT``
+ The call timed out before a space became available to store
+ new messages.
+
+ ``ENOMEM``
+ Insufficient memory to complete the operation.
+
+3) Design
+---------
+
+3.1 Struct-based argument passing
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The message buffer parameters (msg_ptr, msg_len, msg_prio) are consolidated
+into struct mq_msg_attrs rather than passed as individual syscall arguments.
+These are then referenced via struct mq_mmsg_attrs, which contains an
+iovec msg_attrs_vec and a vlen count. The base of msg_attrs_vec holds individual
+mq_msg_attrs entries, and length store struct mq_msg_attrs size.
+
+
+3.2 Compat handling
+~~~~~~~~~~~~~~~~~~~~
+
+.. code-block:: c
+
+ struct compat_msg_attrs {
+ compat_size_t msg_len;
+ compat_uptr_t msg_prio;
+ compat_uptr_t msg_ptr;
+ };
+
+ struct compat_mq_mmsg_attrs {
+ compat_uptr_t msg_attrs_vec;
+ compat_size_t vlen;
+ compat_uptr_t ret;
+ };
+
+
+The compat entry point performs the necessary conversions before
+calling the shared do_mq_sendmmsg() implementation.
+
+
+3.3 start_idx argument
+~~~~~~~~~~~~~~~~~~~
+
+The start_idx argument specifies which message to begin operating on within the queue.
+If a batch request fails partway through, user space is expected to supply an updated
+start_idx reflecting the number of messages already handled, failing to do so may result
+in duplicate message passed to queues.
+
+4) Test Specification
+---------------------
+
+Tests for mq_sendmmsg() should cover the following:
+
+1) Offset and boundary handling — Verify that the implementation work correctly as per
+batch size and start_idx boundary conditions.
+
+2) Partial success — Verify that when an error occurs mid-batch, the call returns
+successfully with the count of messages processed so far.
+
+3) Batch Timeout - Verify batch absolute deadline return expected value when
+queue is completely full.
+
+
+
diff --git a/ipc/msgutil.c b/ipc/msgutil.c
index f137fb2046a7..80d1bb22814b 100644
--- a/ipc/msgutil.c
+++ b/ipc/msgutil.c
@@ -150,8 +150,7 @@ struct msg_msg *copy_msg(struct msg_msg *src, struct msg_msg *dst, size_t len)
dst->m_type = src->m_type;
dst->m_ts = src->m_ts;
return dst;
-
- }
+}
int store_msg(void __user *dest, struct msg_msg *msg, size_t len)
{
diff --git a/tools/testing/selftests/ipc/Makefile b/tools/testing/selftests/ipc/Makefile
index fad10f2bb57b..04da0efd67d7 100644
--- a/tools/testing/selftests/ipc/Makefile
+++ b/tools/testing/selftests/ipc/Makefile
@@ -12,7 +12,7 @@ endif
CFLAGS += $(KHDR_INCLUDES)
-TEST_GEN_PROGS := msgque
+TEST_GEN_PROGS := msgque mq_recvmmsg mq_sendmmsg
include ../lib.mk
diff --git a/tools/testing/selftests/ipc/mq_recvmmsg.c b/tools/testing/selftests/ipc/mq_recvmmsg.c
new file mode 100644
index 000000000000..2f5632923333
--- /dev/null
+++ b/tools/testing/selftests/ipc/mq_recvmmsg.c
@@ -0,0 +1,391 @@
+// SPDX-License-Identifier: GPL-2.0
+#define _GNU_SOURCE
+
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/syscall.h>
+#include <sys/uio.h>
+#include <time.h>
+#include <unistd.h>
+#include "kselftest.h"
+#include <mqueue.h>
+
+#ifndef __NR_mq_recvmmsg
+#if defined(__alpha__)
+#define __NR_mq_recvmmsg 582
+#else
+#define __NR_mq_recvmmsg 472
+#endif
+#endif
+
+#define MQ_PEEK 0x02
+#define MQ_RECV 0x04
+
+#define MQ_NAME_PREFIX "/mq_peek_test"
+#define MQ_MAXMSG 16
+#define MAX_MSG_SIZE 8192
+
+struct msg_attrs {
+ long msg_len;
+ unsigned int *msg_prio;
+ void *msg_ptr;
+};
+
+struct mmsg_attrs {
+ struct iovec *msg_attrs_vec;
+ long vlen;
+ int *err;
+};
+
+struct batch_slot{
+ struct msg_attrs desc;
+ struct iovec iov;
+ char buf[MAX_MSG_SIZE];
+ unsigned int prio;
+};
+
+static long mq_recvmmsg(mqd_t mqdes, struct mmsg_attrs *attrs, unsigned int attrs_len,
+ unsigned int flags, unsigned int start_idx,
+ const struct timespec *timeout)
+{
+ return syscall(__NR_mq_recvmmsg, mqdes, attrs, attrs_len, flags,
+ start_idx, timeout);
+}
+
+static long queue_depth(mqd_t mqd)
+{
+ struct mq_attr attr;
+
+ if (mq_getattr(mqd, &attr))
+ return -1;
+
+ return attr.mq_curmsgs;
+}
+
+static mqd_t open_queue(const char *suffix)
+{
+ static unsigned int seq;
+ char name[128];
+ struct mq_attr attr = {
+ .mq_flags = 0,
+ .mq_maxmsg = MQ_MAXMSG,
+ .mq_msgsize = MAX_MSG_SIZE,
+ };
+
+ mqd_t mqd;
+
+ snprintf(name, sizeof(name), "%s%s_%d_%u", MQ_NAME_PREFIX, suffix, getpid(),
+ seq++);
+
+ mq_unlink(name);
+
+ mqd = mq_open(name, O_NONBLOCK | O_RDWR | O_CREAT | O_EXCL, 0600, &attr);
+ if (mqd == (mqd_t)-1) {
+ ksft_test_result_fail("mq_open(%s) failed: %m\n", name);
+ ksft_exit_fail();
+ }
+
+ mq_unlink(name);
+ return mqd;
+}
+
+static void build_slot(struct batch_slot *slot)
+{
+ memset(slot, 0, sizeof(*slot));
+ slot->desc.msg_len = sizeof(slot->buf);
+ slot->desc.msg_prio = &slot->prio;
+ slot->desc.msg_ptr = slot->buf;
+ slot->iov.iov_base = &slot->desc;
+ slot->iov.iov_len = sizeof(slot->desc);
+}
+
+static void build_slots(struct batch_slot *slots, size_t nr)
+{
+ size_t i;
+
+ for (i = 0; i < nr; i++)
+ build_slot(&slots[i]);
+}
+
+static long batch_recv(mqd_t mqd, struct batch_slot *slots, size_t nr,
+ int *err, unsigned int flags, unsigned int start_idx,
+ const struct timespec *timeout)
+{
+ struct iovec vec[MQ_MAXMSG];
+ struct mmsg_attrs attrs;
+ size_t i;
+
+ for (i = 0; i < nr; i++)
+ vec[i] = slots[i].iov;
+
+ attrs.msg_attrs_vec = vec;
+ attrs.vlen = nr;
+ attrs.err = err;
+
+ return mq_recvmmsg(mqd, &attrs, sizeof(attrs), flags, start_idx,
+ timeout);
+}
+
+
+static void send_or_die(mqd_t mqd, unsigned int prio, const char *payload)
+{
+ if (mq_send(mqd, payload, strlen(payload), prio)) {
+ ksft_test_result_fail("mq_send(%s, %u) failed: %m\n",
+ payload, prio);
+ ksft_exit_fail();
+ }
+}
+
+static bool check_slot(const struct batch_slot *slot, const char *payload,
+ unsigned int prio)
+{
+ size_t len = strlen(payload);
+
+ if (slot->prio != prio)
+ return false;
+ if (memcmp(slot->buf, payload, len))
+ return false;
+ return true;
+}
+
+static void test_peek_batch_success_count(void)
+{
+ mqd_t mqd = open_queue("peek_count");
+ struct batch_slot slots[3];
+ long ret;
+ int err;
+
+ send_or_die(mqd, 1, "low");
+ send_or_die(mqd, 9, "high");
+
+ build_slots(slots, ARRAY_SIZE(slots));
+ ret = batch_recv(mqd, slots, ARRAY_SIZE(slots), &err, MQ_PEEK, 0, NULL);
+
+ if (ret != 2) {
+ ksft_test_result_fail("peek count: ret=%ld expected=2\n", ret);
+ goto out;
+ }
+ if (!check_slot(&slots[0], "high", 9) ||
+ !check_slot(&slots[1], "low", 1)) {
+ ksft_test_result_fail("peek count: payload/prio mismatch\n");
+ goto out;
+ }
+ if (queue_depth(mqd) != 2) {
+ ksft_test_result_fail("peek count: queue depth changed to %ld\n",
+ queue_depth(mqd));
+ goto out;
+ }
+
+ ksft_test_result_pass("peek batch returns number of successful messages\n");
+out:
+ mq_close(mqd);
+}
+
+static void test_receive_batch_success_count(void)
+{
+ static const struct timespec expired = { 0, 0 };
+ mqd_t mqd = open_queue("recv_count");
+ struct batch_slot slots[3];
+ long ret;
+ int err;
+
+ send_or_die(mqd, 2, "two");
+ send_or_die(mqd, 7, "seven");
+
+ build_slots(slots, ARRAY_SIZE(slots));
+ ret = batch_recv(mqd, slots, ARRAY_SIZE(slots), &err, MQ_RECV, 0, &expired);
+
+ if (ret != 2) {
+ ksft_test_result_fail("receive count: ret=%ld expected=2\n", ret);
+ goto out;
+ }
+ if (!check_slot(&slots[0], "seven", 7) ||
+ !check_slot(&slots[1], "two", 2)) {
+ ksft_test_result_fail("receive count: payload/prio mismatch\n");
+ goto out;
+ }
+ if (queue_depth(mqd) != 0) {
+ ksft_test_result_fail("receive count: queue depth=%ld expected=0\n",
+ queue_depth(mqd));
+ goto out;
+ }
+
+ ksft_test_result_pass("receive batch returns success count and consumes messages\n");
+out:
+ mq_close(mqd);
+}
+
+static void test_peek_start_idx_uses_absolute_index(void)
+{
+ mqd_t mqd = open_queue("peek_offset");
+ struct batch_slot slots[3];
+ long ret;
+ int err;
+
+ send_or_die(mqd, 9, "high");
+ send_or_die(mqd, 5, "mid");
+ send_or_die(mqd, 1, "low");
+
+ build_slots(slots, ARRAY_SIZE(slots));
+ memset(slots[0].buf, 0x5a, sizeof(slots[0].buf));
+ ret = batch_recv(mqd, slots, ARRAY_SIZE(slots), &err, MQ_PEEK, 1, NULL);
+
+ if (ret != 2) {
+ ksft_test_result_fail("peek start_idx: ret=%ld expected=2\n", ret);
+ goto out;
+ }
+ if (!check_slot(&slots[1], "mid", 5) ||
+ !check_slot(&slots[2], "low", 1)) {
+ ksft_test_result_fail("peek start_idx: wrong messages returned\n");
+ goto out;
+ }
+ if (slots[0].buf[0] != 0x5a) {
+ ksft_test_result_fail("peek start_idx: slot 0 was written by kernel unnecessarily\n");
+ goto out;
+ }
+
+ ksft_test_result_pass("peek start_idx maps directly to queue index and offset handling seems correct\n");
+out:
+ mq_close(mqd);
+}
+
+static void test_receive_start_idx_not_receives_from_outside_range(void)
+{
+ mqd_t mqd = open_queue("recv_offset");
+ struct batch_slot slots[3];
+ long ret;
+ int err;
+
+ send_or_die(mqd, 8, "first");
+ send_or_die(mqd, 3, "second");
+
+ build_slots(slots, ARRAY_SIZE(slots));
+ memset(slots[0].buf, 0x6b, sizeof(slots[0].buf));
+ ret = batch_recv(mqd, slots, ARRAY_SIZE(slots), &err, MQ_RECV, 1, NULL);
+
+ if (ret != 2) {
+ ksft_test_result_fail("receive start_idx: ret=%ld expected=2\n",
+ ret);
+ goto out;
+ }
+ if (!check_slot(&slots[1], "first", 8) ||
+ !check_slot(&slots[2], "second", 3)) {
+ ksft_test_result_fail("receive start_idx: wrong receive order\n");
+ goto out;
+ }
+ if (slots[0].buf[0] != 0x6b) {
+ ksft_test_result_fail("receive start_idx: slot 0 was written by kernel unnecessarily\n");
+ goto out;
+ }
+
+ ksft_test_result_pass("non-peek path ignores queue indexing and receives from head within range\n");
+out:
+ mq_close(mqd);
+}
+
+static void test_peek_partial_success_on_small_buffer(void)
+{
+ mqd_t mqd = open_queue("peek_small");
+ struct batch_slot slots[2];
+ long ret;
+ int err;
+
+ send_or_die(mqd, 9, "abc");
+ send_or_die(mqd, 4, "1234");
+
+ build_slots(slots, ARRAY_SIZE(slots));
+ slots[1].desc.msg_len = 1;
+ ret = batch_recv(mqd, slots, ARRAY_SIZE(slots), &err, MQ_PEEK, 0, NULL);
+
+ if (ret != 1) {
+ ksft_test_result_fail("peek partial small buffer: ret=%ld expected=1\n",
+ ret);
+ goto out;
+ }
+ if (!check_slot(&slots[0], "abc", 9)) {
+ ksft_test_result_fail("peek partial small buffer: first slot mismatch\n");
+ goto out;
+ }
+ if (queue_depth(mqd) != 2) {
+ ksft_test_result_fail("peek partial small buffer: queue depth=%ld expected=2\n",
+ queue_depth(mqd));
+ goto out;
+ }
+
+ ksft_test_result_pass("peek batch reports completed so far before EMSGSIZE\n");
+out:
+ mq_close(mqd);
+}
+
+static void test_receive_partial_success_on_bad_desc_len(void)
+{
+ mqd_t mqd = open_queue("recv_desc");
+ struct batch_slot slots[2];
+ long ret;
+ int err;
+
+ send_or_die(mqd, 6, "driver");
+ send_or_die(mqd, 2, "module");
+
+ build_slots(slots, ARRAY_SIZE(slots));
+ slots[1].iov.iov_len = sizeof(slots[1].desc) - 1;
+ ret = batch_recv(mqd, slots, ARRAY_SIZE(slots), &err, MQ_RECV, 0, NULL);
+
+ if (ret != 1) {
+ ksft_test_result_fail("receive partial desc len: ret=%ld expected=1\n",
+ ret);
+ goto out;
+ }
+ if (!check_slot(&slots[0], "driver", 6)) {
+ ksft_test_result_fail("receive partial desc len: first slot mismatch\n");
+ goto out;
+ }
+ if (queue_depth(mqd) != 1) {
+ ksft_test_result_fail("receive partial desc len: queue depth=%ld expected=1\n",
+ queue_depth(mqd));
+ goto out;
+ }
+
+ ksft_test_result_pass("receive batch reports completed so far before descriptor error\n");
+out:
+ mq_close(mqd);
+}
+
+static const struct {
+ const char *name;
+ void (*fn)(void);
+} tests[] = {
+ { "peek batch success count", test_peek_batch_success_count },
+ { "receive batch success count", test_receive_batch_success_count },
+ { "peek absolute index with start_idx", test_peek_start_idx_uses_absolute_index },
+ { "receive start_idx obey offset boundary", test_receive_start_idx_not_receives_from_outside_range },
+ { "peek partial success before EMSGSIZE", test_peek_partial_success_on_small_buffer },
+ { "receive partial success before desc error", test_receive_partial_success_on_bad_desc_len },
+};
+
+int main(void)
+{
+ size_t i;
+ struct batch_slot probe;
+ long ret;
+ int err;
+
+ ksft_print_header();
+
+ build_slot(&probe);
+ ret = batch_recv((mqd_t)-1, &probe, 1, &err, MQ_PEEK, 0, NULL);
+ if (ret == -1 && errno == ENOSYS)
+ ksft_exit_skip("mq_recvmmsg syscall not available\n");
+
+ ksft_set_plan(ARRAY_SIZE(tests));
+
+ for (i = 0; i < ARRAY_SIZE(tests); i++) {
+ ksft_print_msg("[%02zu] %s\n", i + 1, tests[i].name);
+ tests[i].fn();
+ }
+
+ return ksft_get_fail_cnt() ? 1 : 0;
+}
diff --git a/tools/testing/selftests/ipc/mq_sendmmsg.c b/tools/testing/selftests/ipc/mq_sendmmsg.c
new file mode 100644
index 000000000000..b3aa7eba56a3
--- /dev/null
+++ b/tools/testing/selftests/ipc/mq_sendmmsg.c
@@ -0,0 +1,360 @@
+// SPDX-License-Identifier: GPL-2.0
+#define _GNU_SOURCE
+
+#include <fcntl.h>
+#include <mqueue.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/syscall.h>
+#include <sys/uio.h>
+#include <time.h>
+#include <unistd.h>
+#include "kselftest.h"
+
+#ifndef __NR_mq_sendmmsg
+#if defined(__alpha__)
+#define __NR_mq_sendmmsg 583
+#else
+#define __NR_mq_sendmmsg 473
+#endif
+#endif
+
+#define MQ_NAME_PREFIX "/mq_sendmmsg_test_"
+#define MQ_MAXMSG 16
+#define MAX_MSG_SIZE 128
+
+struct msg_attrs {
+ long msg_len;
+ unsigned int *msg_prio;
+ void *msg_ptr;
+};
+
+struct mmsg_attrs {
+ struct iovec *msg_attrs_vec;
+ long vlen;
+ int *err;
+};
+
+struct send_slot {
+ struct msg_attrs desc;
+ struct iovec iov;
+ const char *payload;
+ unsigned int prio;
+};
+
+static long mq_sendmmsg(mqd_t mqdes, struct mmsg_attrs *attrs,
+ unsigned int attrs_len, unsigned long start_idx,
+ const struct timespec *timeout)
+{
+ return syscall(__NR_mq_sendmmsg, mqdes, attrs, attrs_len, start_idx,
+ timeout);
+}
+
+static mqd_t open_queue_rw(const char *suffix, unsigned int O_FLAGS, long maxmsg)
+{
+ static unsigned int seq;
+ char name[128];
+ struct mq_attr attr = {
+ .mq_flags = 0,
+ .mq_maxmsg = maxmsg,
+ .mq_msgsize = MAX_MSG_SIZE,
+ };
+
+ mqd_t mqd;
+
+ snprintf(name, sizeof(name), "%s%s_%d_%u", MQ_NAME_PREFIX, suffix, getpid(), seq++);
+ mq_unlink(name);
+
+ mqd = mq_open(name, O_FLAGS, 0600, &attr);
+ if (mqd == (mqd_t)-1) {
+ ksft_test_result_fail("mq_open(%s) failed: %m\n", name);
+ ksft_exit_fail();
+ }
+
+ mq_unlink(name);
+ return mqd;
+}
+
+static void init_send_slot(struct send_slot *slot, const char *payload,
+ unsigned int prio)
+{
+ memset(slot, 0, sizeof(*slot));
+ slot->payload = payload;
+ slot->prio = prio;
+ slot->desc.msg_len = strlen(payload);
+ slot->desc.msg_prio = &slot->prio;
+ slot->desc.msg_ptr = (void *)payload;
+ slot->iov.iov_base = &slot->desc;
+ slot->iov.iov_len = sizeof(slot->desc);
+}
+
+static void init_send_slots(struct send_slot *slots,
+ const char *const *payloads,
+ const unsigned int *prios, size_t nr)
+{
+ size_t i;
+
+ for (i = 0; i < nr; i++)
+ init_send_slot(&slots[i], payloads[i], prios[i]);
+}
+
+static long batch_send(mqd_t mqd, struct send_slot *slots, size_t nr, int *err,
+ unsigned int start_idx,
+ const struct timespec *timeout)
+{
+ struct iovec vec[MQ_MAXMSG];
+ struct mmsg_attrs attrs;
+ size_t i;
+
+ for (i = 0; i < nr; i++)
+ vec[i] = slots[i].iov;
+
+ attrs.msg_attrs_vec = vec;
+ attrs.vlen = nr;
+ attrs.err = err;
+
+ return mq_sendmmsg(mqd, &attrs, sizeof(attrs), start_idx, timeout);
+}
+
+static long queue_depth(mqd_t mqd)
+{
+ struct mq_attr attr;
+
+ if (mq_getattr(mqd, &attr))
+ return -1;
+
+ return attr.mq_curmsgs;
+}
+
+static bool recv_expect(mqd_t mqd, const char *payload, unsigned int prio)
+{
+ char buf[MAX_MSG_SIZE];
+ unsigned int got_prio = 0;
+ ssize_t ret;
+ size_t len = strlen(payload);
+
+ memset(buf, 0, sizeof(buf));
+ ret = mq_receive(mqd, buf, sizeof(buf), &got_prio);
+ if (ret < 0)
+ return false;
+ if ((size_t)ret != len)
+ return false;
+ if (got_prio != prio)
+ return false;
+ if (memcmp(buf, payload, len))
+ return false;
+ return true;
+}
+
+static void test_start_idx_obeys_offset(void)
+{
+ mqd_t mqd = open_queue_rw("offset", O_NONBLOCK | O_RDWR | O_CREAT | O_EXCL, MQ_MAXMSG);
+ struct send_slot slots[3];
+ const char *payloads[] = { "first", "second", "third" };
+ unsigned int prios[] = { 1, 5, 9 };
+ long ret;
+ int err;
+
+ init_send_slots(slots, payloads, prios, ARRAY_SIZE(slots));
+ ret = batch_send(mqd, slots, ARRAY_SIZE(slots), &err, 1, NULL);
+
+ if (ret != 2) {
+ ksft_test_result_fail("start_idx skip: ret=%ld expected=2\n", ret);
+ goto out;
+ }
+ if (queue_depth(mqd) != 2) {
+ ksft_test_result_fail("start_idx skip: queue depth=%ld expected=2\n",
+ queue_depth(mqd));
+ goto out;
+ }
+ if (!recv_expect(mqd, "third", 9) ||
+ !recv_expect(mqd, "second", 5)) {
+ ksft_test_result_fail("start_idx skip: wrong messages sent\n");
+ goto out;
+ }
+
+ ksft_test_result_pass("start_idx skips earlier descriptors\n");
+out:
+ mq_close(mqd);
+}
+
+static void test_partial_success_on_null_prio(void)
+{
+ mqd_t mqd = open_queue_rw("null_prio", O_NONBLOCK | O_RDWR | O_CREAT | O_EXCL, MQ_MAXMSG);
+ struct send_slot slots[2];
+ const char *payloads[] = { "ok", "bad" };
+ unsigned int prios[] = { 7, 3 };
+ long ret;
+ int err;
+
+ init_send_slots(slots, payloads, prios, ARRAY_SIZE(slots));
+ slots[1].desc.msg_prio = NULL;
+ ret = batch_send(mqd, slots, ARRAY_SIZE(slots), &err, 0, NULL);
+
+ if (ret != 1) {
+ ksft_test_result_fail("partial null prio: ret=%ld expected=1\n", ret);
+ goto out;
+ }
+ if (queue_depth(mqd) != 1 || !recv_expect(mqd, "ok", 7)) {
+ ksft_test_result_fail("partial null prio: queue contents mismatch\n");
+ goto out;
+ }
+
+ ksft_test_result_pass("completed prefix is returned before NULL prio error\n");
+out:
+ mq_close(mqd);
+}
+
+static void test_partial_success_on_bad_desc_len(void)
+{
+ mqd_t mqd = open_queue_rw("bad_desc", O_NONBLOCK | O_RDWR | O_CREAT | O_EXCL, MQ_MAXMSG);
+ struct send_slot slots[2];
+ const char *payloads[] = { "ok", "bad" };
+ unsigned int prios[] = { 8, 2 };
+ long ret;
+ int err;
+
+ init_send_slots(slots, payloads, prios, ARRAY_SIZE(slots));
+ slots[1].iov.iov_len = sizeof(slots[1].desc) - 1;
+ ret = batch_send(mqd, slots, ARRAY_SIZE(slots), &err, 0, NULL);
+
+ if (ret != 1) {
+ ksft_test_result_fail("partial bad desc len: ret=%ld expected=1\n", ret);
+ goto out;
+ }
+ if (queue_depth(mqd) != 1 || !recv_expect(mqd, "ok", 8)) {
+ ksft_test_result_fail("partial bad desc len: queue contents mismatch\n");
+ goto out;
+ }
+
+ ksft_test_result_pass("completed prefix is returned before descriptor error\n");
+out:
+ mq_close(mqd);
+}
+
+static void test_partial_success_on_bad_prio_ptr(void)
+{
+ mqd_t mqd = open_queue_rw("bad_prio_ptr", O_NONBLOCK | O_RDWR | O_CREAT | O_EXCL, MQ_MAXMSG);
+ struct send_slot slots[2];
+ const char *payloads[] = { "ok", "bad" };
+ unsigned int prios[] = { 6, 4 };
+ long ret;
+ int err;
+
+ init_send_slots(slots, payloads, prios, ARRAY_SIZE(slots));
+ slots[1].desc.msg_prio = (void *)1;
+ ret = batch_send(mqd, slots, ARRAY_SIZE(slots), &err, 0, NULL);
+
+ if (ret != 1) {
+ ksft_test_result_fail("partial bad prio ptr: ret=%ld expected=1\n", ret);
+ goto out;
+ }
+ if (queue_depth(mqd) != 1 || !recv_expect(mqd, "ok", 6)) {
+ ksft_test_result_fail("partial bad prio ptr: queue contents mismatch\n");
+ goto out;
+ }
+
+ ksft_test_result_pass("completed prefix is returned before bad prio pointer fault\n");
+out:
+ mq_close(mqd);
+}
+
+static void test_full_queue_timeout(void)
+{
+ static const struct timespec expired = { 0, 0 };
+ mqd_t mqd = open_queue_rw("full_timeout", O_RDWR | O_CREAT | O_EXCL, 1);
+ struct send_slot slot;
+ long ret;
+ int err;
+
+ init_send_slot(&slot, "first", 1);
+ ret = batch_send(mqd, &slot, 1, &err, 0, NULL);
+ if (ret != 1)
+ ksft_exit_fail_msg("failed to prefill queue\n");
+
+ init_send_slot(&slot, "second", 2);
+ ret = batch_send(mqd, &slot, 1, &err, 0, &expired);
+
+ if (ret == -1 && errno == ETIMEDOUT && queue_depth(mqd) == 1)
+ ksft_test_result_pass("full queue with expired timeout returns ETIMEDOUT\n");
+ else
+ ksft_test_result_fail("full queue timeout: ret=%ld errno=%d depth=%ld\n",
+ ret, errno, queue_depth(mqd));
+
+ mq_close(mqd);
+}
+
+static void test_partial_success_when_queue_fills(void)
+{
+ static const struct timespec expired = { 0, 0 };
+ mqd_t mqd = open_queue_rw("queue_fills", O_NONBLOCK | O_RDWR | O_CREAT | O_EXCL, 2);
+ struct send_slot slots[2];
+ const char *payloads[] = { "fit", "overflow" };
+ unsigned int prios[] = { 1, 2 };
+ long ret;
+ int err;
+
+ init_send_slot(&slots[0], "prefill", 3);
+ ret = batch_send(mqd, &slots[0], 1, &err, 0, NULL);
+ if (ret != 1)
+ ksft_exit_fail_msg("failed to prefill queue\n");
+
+ init_send_slots(slots, payloads, prios, ARRAY_SIZE(slots));
+ ret = batch_send(mqd, slots, ARRAY_SIZE(slots), &err, 0, &expired);
+
+ if (ret != 1) {
+ ksft_test_result_fail("queue fills partial: ret=%ld expected=1\n", ret);
+ goto out;
+ }
+ if (queue_depth(mqd) != 2) {
+ ksft_test_result_fail("queue fills partial: depth=%ld expected=2\n",
+ queue_depth(mqd));
+ goto out;
+ }
+ if (!recv_expect(mqd, "prefill", 3) || !recv_expect(mqd, "fit", 1)) {
+ ksft_test_result_fail("queue fills partial: queue contents mismatch\n");
+ goto out;
+ }
+
+ ksft_test_result_pass("completed operation is returned when queue fills mid-batch\n");
+out:
+ mq_close(mqd);
+}
+
+static const struct {
+ const char *name;
+ void (*fn)(void);
+} tests[] = {
+ { "start_idx obey offset", test_start_idx_obeys_offset },
+ { "partial success on NULL prio", test_partial_success_on_null_prio },
+ { "partial success on bad desc len", test_partial_success_on_bad_desc_len },
+ { "partial success on bad prio pointer", test_partial_success_on_bad_prio_ptr },
+ { "full queue timeout", test_full_queue_timeout },
+ { "partial success when queue fills", test_partial_success_when_queue_fills },
+};
+
+int main(void)
+{
+ size_t i;
+ struct send_slot probe;
+ long ret;
+ int err;
+
+ ksft_print_header();
+
+ init_send_slot(&probe, "probe", 1);
+ ret = batch_send((mqd_t)-1, &probe, 1, &err, 0, NULL);
+ if (ret == -1 && errno == ENOSYS)
+ ksft_exit_skip("mq_sendmmsg syscall not available\n");
+
+ ksft_set_plan(ARRAY_SIZE(tests));
+
+ for (i = 0; i < ARRAY_SIZE(tests); i++) {
+ ksft_print_msg("[%02zu] %s\n", i + 1, tests[i].name);
+ tests[i].fn();
+ }
+
+ return ksft_get_fail_cnt() ? 1 : 0;
+}
--
2.43.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCH v3 1/4] IPC: Added two new system call mq_recvmmsg() and mq_sendmmsg()
2026-06-20 11:23 ` [PATCH v3 1/4] IPC: Added two new system call mq_recvmmsg() and mq_sendmmsg() Mathura_Kumar
@ 2026-06-20 20:29 ` Andrei Vagin
0 siblings, 0 replies; 12+ messages in thread
From: Andrei Vagin @ 2026-06-20 20:29 UTC (permalink / raw)
To: Mathura_Kumar; +Cc: criu, ptikhomirov
Hi Mathura_Kumar,
I haven't had a chance to look closely at the patches yet. Here is the
sashiko-like report.
Please review these comments for relevance.
> diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h
> index 02c88cbb4582..188ac8547e5a 100644
> --- a/include/linux/syscalls.h
> +++ b/include/linux/syscalls.h
> @@ -739,7 +743,13 @@ asmlinkage long sys_sysinfo(struct sysinfo __user *info);
> 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 __user *abs_timeout);
> +asmlinkage long sys_mq_sendmmsg(mqd_t mqdes, struct mq_mmsg_attrs __user *attrs,
> + unsigned int attrs_len, unsigned int flags, unsigned long start_index,
> + const struct __kernel_timespec __user *abs_timeout);
> asmlinkage long sys_mq_timedreceive(mqd_t mqdes, char __user *msg_ptr, size_t msg_len, unsigned int __user *msg_prio, const struct __kernel_timespec __user *abs_timeout);
There seems to be a mismatch between this declaration and the actual
implementation of sys_mq_sendmmsg in ipc/mqueue.c.
The declaration above includes an extra "flags" argument (6 arguments total),
but the implementation uses SYSCALL_DEFINE5 (5 arguments total, without flags):
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)
This mismatch will cause argument shifting when the syscall is invoked,
leading to incorrect parameter interpretation (e.g. start_index being treated
as the timeout pointer) and likely crashes or EFAULT.
Should the declaration be updated to match the 5-argument implementation?
The compat declaration in include/linux/compat.h also seems to have the same
issue.
[ ... ]
> diff --git a/ipc/mqueue.c b/ipc/mqueue.c
> index 4798b375972b..99c0aa3d9c9d 100644
> --- a/ipc/mqueue.c
> +++ b/ipc/mqueue.c
> @@ -1380,6 +1380,12 @@ static ssize_t do_mq_recvmsg2(mqd_t mqdes, struct mq_msg_attrs *args, unsigned i
> k_msg_buffer = alloc_msg(k_m_ts);
>
> if (!k_msg_buffer)
> return -ENOMEM;
> ret = security_msg_msg_alloc(k_msg_buffer);
> if (ret)
> return ret;
If security_msg_msg_alloc() fails, it returns an error code, but it doesn't
seem to free the recently allocated k_msg_buffer.
Does this leak the k_msg_buffer? Should it call free_msg(k_msg_buffer) before
returning?
[ ... ]
> diff --git a/ipc/msgutil.c b/ipc/msgutil.c
> index e28f0cecb2ec..f137fb2046a7 100644
> --- a/ipc/msgutil.c
> +++ b/ipc/msgutil.c
> @@ -122,39 +122,37 @@ struct msg_msg *load_msg(const void __user *src, size_t len)
>
> +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 = 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 = min(len, DATALEN_MSG);
> - memcpy(dst + 1, src + 1, alen);
> + chunk = min(len, DATALEN_MSG);
>
> - for (dst_pseg = dst->next, src_pseg = src->next;
> - src_pseg != NULL;
> - dst_pseg = dst_pseg->next, src_pseg = src_pseg->next) {
> + memcpy(dst + 1, src + 1, chunk);
> + remaining = len - chunk;
> + src_seg = src->next;
> + dst_seg = dst->next;
>
> - len -= alen;
> - alen = min(len, DATALEN_SEG);
> - memcpy(dst_pseg + 1, src_pseg + 1, alen);
> + while (remaining > 0 && src_seg && dst_seg) {
> + chunk = min(remaining, DATALEN_SEG);
> + memcpy(dst_seg + 1, src_seg + 1, chunk);
> + remaining -= chunk;
> + src_seg = src_seg->next;
> + dst_seg = dst_seg->next;
> }
> -
> + if (remaining != 0)
> + return ERR_PTR(-EINVAL);
> dst->m_type = src->m_type;
> - dst->m_ts = src->m_ts;
> -
> + dst->m_ts = src->m_ts;
> return dst;
> }
In the updated copy_msg() helper, the "len" parameter specifies how many
bytes to copy, and it is validated to be less than or equal to src->m_ts.
If copy_msg() is called with "len" less than src->m_ts, it will copy only
"len" bytes, but the destination message size (dst->m_ts) is still set to
src->m_ts.
If the destination buffer was allocated based on "len" (which is smaller
than src->m_ts), setting dst->m_ts to a larger value might lead to out-of-bounds
reads when dst->m_ts is used later to copy the message to user space.
Should dst->m_ts be set to "len" instead of "src->m_ts"?
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v3 1/4] IPC: Added two new system call mq_recvmmsg() and mq_sendmmsg()
2026-06-20 22:36 [PATCH v3 0/4] Add two new system call mq_recvmmsg() and mq_sendmmsg() to posix ipc mqueue Mathura_Kumar
@ 2026-06-20 22:36 ` Mathura_Kumar
0 siblings, 0 replies; 12+ messages in thread
From: Mathura_Kumar @ 2026-06-20 22:36 UTC (permalink / raw)
To: criu; +Cc: academic1mathura, avagin, ptikhomirov
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 discrete
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
Signed-off-by: Mathura_Kumar <academic1mathura@gmail.com>
---
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_mmsg_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_mmsg_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 >= 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 *info);
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 __user *abs_timeout);
+asmlinkage long sys_mq_sendmmsg(mqd_t mqdes, struct mq_mmsg_attrs __user *attrs,
+ unsigned int attrs_len, unsigned long start_index,
+ const struct __kernel_timespec __user *abs_timeout);
asmlinkage long sys_mq_timedreceive(mqd_t mqdes, char __user *msg_ptr, size_t msg_len, unsigned int __user *msg_prio, const struct __kernel_timespec __user *abs_timeout);
+asmlinkage long sys_mq_recvmmsg(mqd_t mqdes, struct mq_mmsg_attrs __user *attrs,
+ unsigned int attrs_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 __user *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 passed
diff --git a/ipc/mqueue.c b/ipc/mqueue.c
index 4798b375972b..d58591acdaac 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 priority */
+ unsigned int subtree_msg_count; /* sum of messages in this node and all descendants */
};
/*
@@ -186,7 +194,41 @@ static struct ipc_namespace *get_ns_from_inode(struct 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)->subtree_msg_count;
+}
+
+static void msg_tree_propagate_subtree_msg_count(struct rb_node *node, struct rb_node *stop)
+{
+ while (node != stop) {
+ struct posix_msg_tree_node *leaf = rb_entry(node, struct posix_msg_tree_node, rb_node);
+ unsigned int new_count = leaf->msg_count +
+ get_subtree_count(node->rb_left) +
+ get_subtree_count(node->rb_right);
+ if (leaf->subtree_msg_count == new_count)
+ break;
+ leaf->subtree_msg_count = new_count;
+ node = 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 = rb_entry(old, struct posix_msg_tree_node, rb_node);
+ struct posix_msg_tree_node *new_leaf = rb_entry(new, struct posix_msg_tree_node, rb_node);
+
+ new_leaf->subtree_msg_count = old_leaf->subtree_msg_count;
+}
+
+static const struct rb_augment_callbacks msg_tree_callbacks = {
+ .propagate = msg_tree_propagate_subtree_msg_count,
+ .copy = msg_tree_copy_subtree_msg_count,
+ .rotate = msg_tree_propagate_subtree_msg_count,
+};
+
static int msg_insert(struct msg_msg *msg, struct mqueue_inode_info *info)
{
struct rb_node **p, *parent = NULL;
@@ -216,13 +258,19 @@ static int msg_insert(struct msg_msg *msg, struct mqueue_inode_info *info)
INIT_LIST_HEAD(&leaf->msg_list);
}
leaf->priority = msg->m_type;
+ leaf->msg_count = 1;
+ leaf->subtree_msg_count = 1;
if (rightmost)
info->msg_tree_rightmost = &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_callbacks);
+ 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 += 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_tree_node *leaf,
if (info->msg_tree_rightmost == node)
info->msg_tree_rightmost = 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 = 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_node, 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] = 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 = NULL;
struct posix_msg_tree_node *new_leaf = NULL;
- int ret = 0;
+ ssize_t ret = 0;
DEFINE_WAKE_Q(wake_q);
if (unlikely(msg_prio >= (unsigned long) MQ_PRIO_MAX))
return -EINVAL;
- if (ts) {
- expires = timespec64_to_ktime(*ts);
- timeout = &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 char __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, unsigned 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 = NULL;
- struct posix_msg_tree_node *new_leaf = NULL;
if (ts) {
expires = timespec64_to_ktime(*ts);
timeout = &expires;
}
+ return do_mq_sendmsg(mqdes, u_msg_ptr, msg_len, msg_prio, ts, timeout);
+}
+
+static ssize_t do_mq_recvmsg(mqd_t mqdes, char __user *u_msg_ptr, size_t msg_len,
+ unsigned int __user *u_msg_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 = 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, size_t msg_len,
+ unsigned int __user *u_msg_prio, struct timespec64 *ts)
+{
+ ktime_t expires, *timeout = NULL;
+
+ if (ts) {
+ expires = timespec64_to_ktime(*ts);
+ timeout = &expires;
+ }
+
+ return do_mq_recvmsg(mqdes, u_msg_ptr, msg_len, u_msg_prio, ts, timeout);
+}
+
+static struct msg_msg *mq_peek_index(struct mqueue_inode_info *info, unsigned 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 = 0;
+
+ node = info->msg_tree.rb_node;
+ while (node) {
+ leaf = rb_entry(node, struct posix_msg_tree_node, rb_node);
+ right_count = get_subtree_count(node->rb_right);
+
+ if (index < right_count) {
+ node = node->rb_right;
+ } else if (index < (right_count + leaf->msg_count)) {
+ /* Target is at this priority level */
+ offset = index - right_count;
+ list_for_each_entry(msg, &leaf->msg_list, m_list) {
+ if (i == offset)
+ return msg;
+ i++;
+ }
+ break;
+ } else {
+ index -= (right_count + leaf->msg_count);
+ node = node->rb_left;
+ }
+ }
+
+ return NULL;
+}
+
+static ssize_t do_mq_recvmsg2(mqd_t mqdes, struct mq_msg_attrs *args, unsigned int flags,
+ unsigned long index, const 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 = file_inode(fd_file(f));
+ if (unlikely(fd_file(f)->f_op != &mqueue_file_operations))
+ return -EBADF;
+
+ info = 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 >= (unsigned long)info->attr.mq_maxmsg)
+ return -EINVAL;
+
+ spin_lock(&info->lock);
+
+ if (info->attr.mq_curmsgs == 0) {
+ spin_unlock(&info->lock);
+ return -EAGAIN;
+ }
+ msg_ptr = mq_peek_index(info, index);
+ if (!msg_ptr) {
+ spin_unlock(&info->lock);
+ return -ENODATA;
+ }
+ k_m_type = msg_ptr->m_type;
+ k_m_ts = msg_ptr->m_ts;
+
+ spin_unlock(&info->lock);
+
+ k_msg_buffer = alloc_msg(k_m_ts);
+
+ if (!k_msg_buffer)
+ return -ENOMEM;
+ ret = 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 memory
+ * allocation and premature allocation before confirming
+ * a message actually exists to peek and retrieving required buffer size
+ * when first lock was taken.
+ */
+ spin_lock(&info->lock);
+
+ msg_ptr = mq_peek_index(info, index);
+ if (!msg_ptr || msg_ptr->m_type != k_m_type ||
+ msg_ptr->m_ts != k_m_ts) {
+ spin_unlock(&info->lock);
+ free_msg(k_msg_buffer);
+ return -EAGAIN;
+ }
+ msg_ptr = 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 = 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,
+ const struct compat_mq_mmsg_attrs __user *uattrs,
+ unsigned int attrs_len)
+{
+ struct compat_mq_mmsg_attrs v = {};
+ int err;
+
+ if (unlikely(attrs_len < sizeof(v)))
+ return -EINVAL;
+ if (unlikely(attrs_len > PAGE_SIZE))
+ return -E2BIG;
+ err = copy_struct_from_user(&v, sizeof(v), uattrs, attrs_len);
+
+ if (err)
+ return err;
+ attrs->msg_attrs_vec = (struct iovec __user *)compat_ptr((unsigned long)v.msg_attrs_vec);
+ attrs->ret = (int *)compat_ptr(v.ret);
+ attrs->vlen = 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 = {};
+
+ if (desc_iov->iov_len != sizeof(v))
+ return -EINVAL;
+ if (copy_from_user(&v, desc_iov->iov_base, sizeof(v)))
+ return -EFAULT;
+
+ attr->msg_len = v.msg_len;
+ attr->msg_prio = (unsigned int *)compat_ptr(v.msg_prio);
+ attr->msg_ptr = compat_ptr(v.msg_ptr);
+ return 0;
+ }
+
+#endif
+
+static int mq_mmsg_copy_msg_attr(struct mq_msg_attrs *attr,
+ const struct iovec *desc_iov, bool compat)
+{
+ if (compat)
+ return mq_mmsg_copy_compat_msg_attr(attr, desc_iov);
+ if (desc_iov->iov_len != 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 *attrs,
+ unsigned int flags, unsigned long start_idx,
+ struct timespec64 *ts, bool compat)
+{
+ struct iov_iter iter;
+ struct iovec outer_iovstack[UIO_FASTIOV], *free_iov = outer_iovstack;
+ const struct iovec *msg_iov;
+ ktime_t batch_deadline, *timeout = NULL;
+ ssize_t ret = 0;
+ ssize_t batch_success = 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 = __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 = iter_iov(&iter);
+
+ /* One absolute deadline for the whole batch. */
+ if (ts) {
+ batch_deadline = timespec64_to_ktime(*ts);
+ timeout = &batch_deadline;
+ }
+ for (i = start_idx; i < attrs->vlen; i++) {
+ struct mq_msg_attrs desc = {};
+
+ ret = mq_mmsg_copy_msg_attr(&desc, &msg_iov[i], compat);
+ if (ret < 0)
+ break;
+
+ index = (flags & MQ_PEEK) ? i : 0;
+ ret = do_mq_recvmsg2(mqdes, &desc, flags, index, ts, timeout);
+
+ if (ret < 0) {
+ if (attrs->ret && put_user(ret, attrs->ret)) {
+ kfree(free_iov);
+ return -EFAULT;
+ }
+ break;
+ }
+ batch_success++;
+ }
+
+ if (!(batch_success != 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 *attrs,
+ unsigned long start_idx, struct timespec64 *ts,
+ bool compat)
+{
+ struct iov_iter iter;
+ struct iovec outer_iovstack[UIO_FASTIOV], *free_iov = outer_iovstack;
+ const struct iovec *msg_iov;
+ ktime_t batch_deadline, *timeout = NULL;
+ ssize_t ret = 0;
+ ssize_t batch_success = 0;
+ unsigned int i;
+
+ if (!attrs->vlen || attrs->vlen > UIO_MAXIOV || start_idx > attrs->vlen)
+ return -EINVAL;
+
+ ret = __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 = iter_iov(&iter);
+
+ if (ts) {
+ batch_deadline = timespec64_to_ktime(*ts);
+ timeout = &batch_deadline;
+ }
+ for (i = start_idx; i < attrs->vlen; i++) {
+ struct mq_msg_attrs desc = {};
+ unsigned int msg_prio = 0;
+
+ ret = mq_mmsg_copy_msg_attr(&desc, &msg_iov[i], compat);
+ if (ret < 0)
+ break;
+ if (!desc.msg_prio) {
+ ret = -EINVAL;
+ break;
+ }
+ if (get_user(msg_prio, desc.msg_prio)) {
+ ret = -EFAULT;
+ break;
+ }
+ ret = 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 != 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_ptr,
- 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 = NULL;
+
if (u_abs_timeout) {
int res = prepare_timeout(u_abs_timeout, &ts);
+
if (res)
return res;
p = &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 = {};
+ struct timespec64 ts, *p = NULL;
+ int ret;
+
+ ret = mq_mmsg_copy_attrs_from_user(&kattrs, attrs, attrs_len);
+ if (ret)
+ return ret;
+ if (u_abs_timeout) {
+ int res = prepare_timeout(u_abs_timeout, &ts);
+
+ if (res)
+ return res;
+ p = &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 = NULL;
+
if (u_abs_timeout) {
int res = prepare_timeout(u_abs_timeout, &ts);
+
if (res)
return res;
p = &ts;
@@ -1258,6 +1683,26 @@ SYSCALL_DEFINE5(mq_timedreceive, mqd_t, mqdes, char __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 = {};
+ struct timespec64 ts, *p = NULL;
+ int ret;
+
+ ret = mq_mmsg_copy_attrs_from_user(&kattrs, attrs, attrs_len);
+ if (ret)
+ return ret;
+ if (u_abs_timeout) {
+ int res = prepare_timeout(u_abs_timeout, &ts);
+
+ if (res)
+ return res;
+ p = &ts;
+ }
+ return do_mq_recvmmsg(mqdes, &kattrs, flags, start_idx, p, false);
+}
/*
* Notes: the case when user wants us to deregister (with NULL as pointer)
* and he isn't currently owner of notification, will be silently discarded.
@@ -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)
+ const 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)
+ struct compat_mq_attr __user *uattr)
{
struct compat_mq_attr v;
@@ -1541,11 +1986,61 @@ COMPAT_SYSCALL_DEFINE3(mq_getsetattr, mqd_t, mqdes,
return -EFAULT;
return 0;
}
+
+COMPAT_SYSCALL_DEFINE5(mq_sendmmsg, mqd_t, mqdes, struct compat_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 = {};
+ struct timespec64 ts, *p = NULL;
+ struct iovec msg_atrrs_vec = {};
+ int ret;
+
+ kattrs.msg_attrs_vec = &msg_atrrs_vec;
+ ret = mq_mmsg_copy_compat_attrs(&kattrs, attrs, attrs_len);
+ if (ret)
+ return ret;
+
+ if (u_abs_timeout) {
+ int res = prepare_timeout(u_abs_timeout, &ts);
+
+ if (res)
+ return res;
+ p = &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, unsigned int, flags, unsigned long, start_idx,
+ const struct __kernel_timespec __user *, u_abs_timeout)
+{
+ struct mq_mmsg_attrs kattrs = {};
+ struct timespec64 ts, *p = NULL;
+ struct iovec msg_atrrs_vec = {};
+ int ret;
+
+ kattrs.msg_attrs_vec = &msg_atrrs_vec;
+ ret = mq_mmsg_copy_compat_attrs(&kattrs, attrs, attrs_len);
+ if (ret)
+ return ret;
+
+ if (u_abs_timeout) {
+ int res = prepare_timeout(u_abs_timeout, &ts);
+
+ if (res)
+ return res;
+ p = &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 timespec64 *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 = NULL;
+
if (u_abs_timeout) {
int res = compat_prepare_timeout(u_abs_timeout, &ts);
+
if (res)
return res;
p = &ts;
@@ -1575,14 +2072,17 @@ SYSCALL_DEFINE5(mq_timedreceive_time32, mqd_t, mqdes,
const struct old_timespec32 __user *, u_abs_timeout)
{
struct timespec64 ts, *p = NULL;
+
if (u_abs_timeout) {
int res = compat_prepare_timeout(u_abs_timeout, &ts);
+
if (res)
return res;
p = &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 = {
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 = copy_msg(msg, copy);
+ msg = 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, size_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 = 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 = min(len, DATALEN_MSG);
- memcpy(dst + 1, src + 1, alen);
+ chunk = min(len, DATALEN_MSG);
- for (dst_pseg = dst->next, src_pseg = src->next;
- src_pseg != NULL;
- dst_pseg = dst_pseg->next, src_pseg = src_pseg->next) {
+ memcpy(dst + 1, src + 1, chunk);
+ remaining = len - chunk;
+ src_seg = src->next;
+ dst_seg = dst->next;
- len -= alen;
- alen = min(len, DATALEN_SEG);
- memcpy(dst_pseg + 1, src_pseg + 1, alen);
+ while (remaining > 0 && src_seg && dst_seg) {
+ chunk = min(remaining, DATALEN_SEG);
+ memcpy(dst_seg + 1, src_seg + 1, chunk);
+ remaining -= chunk;
+ src_seg = src_seg->next;
+ dst_seg = dst_seg->next;
}
-
+ if (remaining != 0)
+ return ERR_PTR(-EINVAL);
dst->m_type = src->m_type;
- dst->m_ts = src->m_ts;
-
+ dst->m_ts = 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
^ permalink raw reply related [flat|nested] 12+ messages in thread
end of thread, other threads:[~2026-06-20 22:36 UTC | newest]
Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-20 11:23 [PATCH v3 0/4] Add two new system call mq_recvmmsg() and mq_sendmmsg() to posix ipc mqueue Mathura_Kumar
2026-06-20 11:23 ` [PATCH v3 1/4] IPC: Added two new system call mq_recvmmsg() and mq_sendmmsg() Mathura_Kumar
2026-06-20 20:29 ` Andrei Vagin
2026-06-20 11:23 ` [PATCH v3 2/4] IPC: Added system call entry in all of most common architectures Mathura_Kumar
2026-06-20 11:23 ` [PATCH v3 3/4] IPC: Added system call entry in performance tool Mathura_Kumar
2026-06-20 11:23 ` [PATCH v3 4/4] Test: Added self-testing and documentation Mathura_Kumar
-- strict thread matches above, loose matches on Subject: below --
2026-06-20 22:36 [PATCH v3 0/4] Add two new system call mq_recvmmsg() and mq_sendmmsg() to posix ipc mqueue Mathura_Kumar
2026-06-20 22:36 ` [PATCH v3 1/4] IPC: Added two new system call mq_recvmmsg() and mq_sendmmsg() Mathura_Kumar
2026-06-12 5:20 [PATCH v3 0/4] Add two new system call mq_recvmmsg() and mq_sendmmsg() to posix ipc mqueue Mathura_Kumar
2026-06-12 5:20 ` [PATCH v3 1/4] IPC: Added two new system call mq_recvmmsg() and mq_sendmmsg() Mathura_Kumar
2026-06-12 11:49 ` Pavel Tikhomirov
[not found] ` <CA+QNo20DfeHOVYq4XgyCaUU4-3AYxh+tDyymjCD1DLkw7zytrA@mail.gmail.com>
2026-06-12 12:35 ` Mathura
2026-06-12 13:52 ` Pavel Tikhomirov
2026-06-14 14:37 ` Mathura
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox