* [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
2026-06-12 5:20 ` [PATCH v3 2/4] IPC: Added system call entry in all of most common architectures Mathura_Kumar
` (2 subsequent siblings)
3 siblings, 1 reply; 11+ 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] 11+ 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; 11+ 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] 11+ messages in thread
* [PATCH v3 2/4] IPC: Added system call entry in all of most common architectures
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 5:20 ` Mathura_Kumar
2026-06-12 5:20 ` [PATCH v3 3/4] IPC: Added system call entry in performance tool Mathura_Kumar
2026-06-12 5:20 ` [PATCH v3 4/4] Test: Added self-testing and documentation Mathura_Kumar
3 siblings, 0 replies; 11+ messages in thread
From: Mathura_Kumar @ 2026-06-12 5:20 UTC (permalink / raw)
To: criu; +Cc: academic1mathura, avagin
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 | 2 ++
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 | 2 ++
arch/sh/kernel/syscalls/syscall.tbl | 2 ++
arch/sparc/kernel/syscalls/syscall.tbl | 2 ++
arch/x86/entry/syscalls/syscall_32.tbl | 2 ++
arch/x86/entry/syscalls/syscall_64.tbl | 3 ++-
arch/xtensa/kernel/syscalls/syscall.tbl | 2 ++
16 files changed, 33 insertions(+), 1 deletion(-)
diff --git a/arch/alpha/kernel/syscalls/syscall.tbl b/arch/alpha/kernel/syscalls/syscall.tbl
index f31b7afffc34..7d02467bdab8 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_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..1641069887ed 100644
--- a/arch/arm64/tools/syscall_32.tbl
+++ b/arch/arm64/tools/syscall_32.tbl
@@ -483,3 +483,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/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..eb64e0d9105f 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
\ No newline at end of file
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..46af2d26fa8d 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
\ No newline at end of file
diff --git a/arch/parisc/kernel/syscalls/syscall.tbl b/arch/parisc/kernel/syscalls/syscall.tbl
index c6331dad9461..5ffe028920e2 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
\ No newline at end of file
diff --git a/arch/powerpc/kernel/syscalls/syscall.tbl b/arch/powerpc/kernel/syscalls/syscall.tbl
index 4fcc7c58a105..1b2683cfd209 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
\ No newline at end of file
diff --git a/arch/s390/kernel/syscalls/syscall.tbl b/arch/s390/kernel/syscalls/syscall.tbl
index 09a7ef04d979..06a03b0488ad 100644
--- a/arch/s390/kernel/syscalls/syscall.tbl
+++ b/arch/s390/kernel/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/arch/sh/kernel/syscalls/syscall.tbl b/arch/sh/kernel/syscalls/syscall.tbl
index 70b315cbe710..3733d033f78c 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
\ No newline at end of file
diff --git a/arch/sparc/kernel/syscalls/syscall.tbl b/arch/sparc/kernel/syscalls/syscall.tbl
index 7e71bf7fcd14..4707e454af4f 100644
--- a/arch/sparc/kernel/syscalls/syscall.tbl
+++ b/arch/sparc/kernel/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/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
--
2.43.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v3 3/4] IPC: Added system call entry in performance tool
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 5:20 ` [PATCH v3 2/4] IPC: Added system call entry in all of most common architectures Mathura_Kumar
@ 2026-06-12 5:20 ` Mathura_Kumar
2026-06-12 5:20 ` [PATCH v3 4/4] Test: Added self-testing and documentation Mathura_Kumar
3 siblings, 0 replies; 11+ messages in thread
From: Mathura_Kumar @ 2026-06-12 5:20 UTC (permalink / raw)
To: criu; +Cc: academic1mathura, avagin
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 | 3 +++
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, 40 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..275803280432 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_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..545aaa1715a3 100644
--- a/tools/perf/arch/mips/entry/syscalls/syscall_n64.tbl
+++ b/tools/perf/arch/mips/entry/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/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] 11+ messages in thread
* [PATCH v3 4/4] Test: Added self-testing and documentation
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
` (2 preceding siblings ...)
2026-06-12 5:20 ` [PATCH v3 3/4] IPC: Added system call entry in performance tool Mathura_Kumar
@ 2026-06-12 5:20 ` Mathura_Kumar
3 siblings, 0 replies; 11+ messages in thread
From: Mathura_Kumar @ 2026-06-12 5:20 UTC (permalink / raw)
To: criu; +Cc: academic1mathura, avagin
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 +++++++++
tools/testing/selftests/ipc/Makefile | 2 +-
tools/testing/selftests/ipc/mq_recvmmsg.c | 391 ++++++++++++++++++++
tools/testing/selftests/ipc/mq_sendmmsg.c | 360 ++++++++++++++++++
6 files changed, 1127 insertions(+), 1 deletion(-)
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..a812e4cfbc3d
--- /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 {
+ struct compat_iovec *msg_attrs_vec;
+ compat_size_t vlen;
+ compat_size_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..960720b30ffc
--- /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 {
+ struct compat_iovec *msg_attrs_vec;
+ compat_size_t vlen;
+ compat_size_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/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..9224f2a2d96a
--- /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 long msg_len;
+ unsigned int *msg_prio;
+ void *msg_ptr;
+};
+
+struct mmsg_attrs {
+ struct iovec *msg_attrs_vec;
+ long 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..ae6765488790
--- /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 {
+ size_t msg_len;
+ unsigned int *msg_prio;
+ void *msg_ptr;
+};
+
+struct mmsg_attrs {
+ struct iovec *msg_attrs_vec;
+ size_t 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] 11+ messages in thread