mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH v4 0/4] Add two new system call mq_recvmmsg() and mq_sendmmsg() to posix ipc mqueue
@ 2026-09-24 15:34 mathura kumar
  2026-09-24 15:34 ` [PATCH v4 1/4] IPC: Added two new system call mq_recvmmsg() and mq_sendmmsg() mathura kumar
                   ` (3 more replies)
  0 siblings, 4 replies; 5+ messages in thread
From: mathura kumar @ 2026-09-24 15:34 UTC (permalink / raw)
  To: brauner; +Cc: linux-kernel, linux-arch, corbet, mathura.kumar.tech



Patch series overview:

  1. Add New system call mq_recvmmsg(), mq_sendmmsg() and handler implementation.
  2. Add system call number in all most common architectures.
  3. Add entries in performance tools.
  4. Prepared Documentation and test.

  I am still awaiting the full review for v3. However, I have incorporated the
  minor feedback suggested so far and wanted to share this updated version.


change since v3:

 - error handling improved for backward compatibility
 - edge cases in augmentation has been handled properly
 - documentation formating fixed and improved
 - syscall number order corrected in arch/microblaze/kernel/syscalls/syscall.tbl

 - v3 Link: https://lore.kernel.org/all/20260816153811.1085261-1-mathura.kumar.tech@gmail.com/


mathura kumar (4):
  IPC: Added two new system call mq_recvmmsg() and  mq_sendmmsg()
  IPC: Added system call entry in all of most common  architectures
  IPC: Added system call entry in performance tool
  Test: Added self-testing and documentation

 Documentation/userspace-api/index.rst         |   2 +
 Documentation/userspace-api/mq_recvmmsg.rst   | 207 ++++++
 Documentation/userspace-api/mq_sendmmsg.rst   | 171 +++++
 arch/alpha/kernel/syscalls/syscall.tbl        |   2 +
 arch/arm/tools/syscall.tbl                    |   2 +
 arch/arm64/tools/syscall_32.tbl               |   3 +
 arch/m68k/kernel/syscalls/syscall.tbl         |   2 +
 arch/microblaze/kernel/syscalls/syscall.tbl   |   2 +
 arch/mips/kernel/syscalls/syscall_n32.tbl     |   2 +
 arch/mips/kernel/syscalls/syscall_n64.tbl     |   3 +
 arch/mips/kernel/syscalls/syscall_o32.tbl     |   2 +
 arch/parisc/kernel/syscalls/syscall.tbl       |   2 +
 arch/powerpc/kernel/syscalls/syscall.tbl      |   2 +
 arch/s390/kernel/syscalls/syscall.tbl         |   3 +
 arch/sh/kernel/syscalls/syscall.tbl           |   2 +
 arch/sparc/kernel/syscalls/syscall.tbl        |   3 +
 arch/x86/entry/syscalls/syscall_32.tbl        |   2 +
 arch/x86/entry/syscalls/syscall_64.tbl        |   3 +-
 arch/xtensa/kernel/syscalls/syscall.tbl       |   2 +
 include/linux/compat.h                        |  12 +-
 include/linux/syscalls.h                      |   9 +
 include/uapi/asm-generic/unistd.h             |   9 +-
 include/uapi/linux/mqueue.h                   |  27 +-
 ipc/mqueue.c                                  | 595 +++++++++++++++++-
 ipc/msg.c                                     |   2 +-
 ipc/msgutil.c                                 |  45 +-
 ipc/util.h                                    |   3 +-
 kernel/sys_ni.c                               |   6 +
 scripts/syscall.tbl                           |   2 +
 tools/include/uapi/asm-generic/unistd.h       |   8 +-
 .../arch/alpha/entry/syscalls/syscall.tbl     |   4 +
 .../perf/arch/arm/entry/syscalls/syscall.tbl  |   3 +
 .../arch/arm64/entry/syscalls/syscall_32.tbl  |   3 +
 .../arch/mips/entry/syscalls/syscall_n64.tbl  |   2 +
 .../arch/parisc/entry/syscalls/syscall.tbl    |   3 +
 .../arch/powerpc/entry/syscalls/syscall.tbl   |   3 +
 .../perf/arch/s390/entry/syscalls/syscall.tbl |   2 +
 tools/perf/arch/sh/entry/syscalls/syscall.tbl |   2 +
 .../arch/sparc/entry/syscalls/syscall.tbl     |   2 +
 .../arch/x86/entry/syscalls/syscall_32.tbl    |   2 +
 .../arch/x86/entry/syscalls/syscall_64.tbl    |   2 +
 .../arch/xtensa/entry/syscalls/syscall.tbl    |   2 +
 tools/scripts/syscall.tbl                     |   2 +
 tools/testing/selftests/ipc/Makefile          |   2 +-
 tools/testing/selftests/ipc/mq_recvmmsg.c     | 392 ++++++++++++
 tools/testing/selftests/ipc/mq_sendmmsg.c     | 360 +++++++++++
 46 files changed, 1855 insertions(+), 66 deletions(-)
 create mode 100644 Documentation/userspace-api/mq_recvmmsg.rst
 create mode 100644 Documentation/userspace-api/mq_sendmmsg.rst
 create mode 100644 tools/testing/selftests/ipc/mq_recvmmsg.c
 create mode 100644 tools/testing/selftests/ipc/mq_sendmmsg.c

-- 
2.43.0


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH v4 1/4] IPC: Added two new system call mq_recvmmsg() and  mq_sendmmsg()
  2026-09-24 15:34 [PATCH v4 0/4] Add two new system call mq_recvmmsg() and mq_sendmmsg() to posix ipc mqueue mathura kumar
@ 2026-09-24 15:34 ` mathura kumar
  2026-09-24 15:35 ` [PATCH v4 2/4] IPC: Added system call entry in all of most common architectures mathura kumar
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 5+ messages in thread
From: mathura kumar @ 2026-09-24 15:34 UTC (permalink / raw)
  To: brauner; +Cc: linux-kernel, linux-arch, corbet, mathura.kumar.tech

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.

  long  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);

  long 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 <mathura.kumar.tech@gmail.com>
---
 include/linux/compat.h            |  12 +-
 include/linux/syscalls.h          |   9 +
 include/uapi/asm-generic/unistd.h |   9 +-
 include/uapi/linux/mqueue.h       |  27 +-
 ipc/mqueue.c                      | 595 ++++++++++++++++++++++++++++--
 ipc/msg.c                         |   2 +-
 ipc/msgutil.c                     |  45 ++-
 ipc/util.h                        |   3 +-
 kernel/sys_ni.c                   |   6 +
 9 files changed, 645 insertions(+), 63 deletions(-)

diff --git a/include/linux/compat.h b/include/linux/compat.h
index 8da0a15c95f4..cb6cda4aebdd 100644
--- a/include/linux/compat.h
+++ b/include/linux/compat.h
@@ -18,7 +18,6 @@
 #include <linux/aio_abi.h>	/* for aio_context_t */
 #include <linux/uaccess.h>
 #include <linux/unistd.h>
-
 #include <asm/compat.h>
 #include <asm/siginfo.h>
 #include <asm/signal.h>
@@ -428,6 +427,8 @@ struct compat_sysctl_args;
 struct compat_kexec_segment;
 struct compat_mq_attr;
 struct compat_msgbuf;
+struct compat_msg_attrs;
+struct compat_mq_mmsg_attrs;
 
 void copy_siginfo_to_external32(struct compat_siginfo *to,
 		const struct kernel_siginfo *from);
@@ -805,8 +806,13 @@ asmlinkage long compat_sys_pwritev64v2(unsigned long fd,
 		const struct iovec __user *vec,
 		unsigned long vlen, loff_t pos, rwf_t flags);
 #endif
-
-
+asmlinkage long compat_sys_mq_sendmmsg(mqd_t mqdes, struct compat_mq_mmsg_attrs __user *attrs,
+				      unsigned int attrs_len, unsigned long start_index,
+				      const struct __kernel_timespec __user *abs_timeout);
+asmlinkage long compat_sys_mq_recvmmsg(mqd_t mqdes, struct compat_mq_mmsg_attrs __user *attrs,
+				      unsigned int attrs_len, unsigned int flags,
+				      unsigned long start_index,
+				      const struct __kernel_timespec __user *abs_timeout);
 /*
  * Deprecated system calls which are still defined in
  * include/uapi/asm-generic/unistd.h and wanted by >= 1 arch
diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h
index 874d9067a43b..0abf938e86da 100644
--- a/include/linux/syscalls.h
+++ b/include/linux/syscalls.h
@@ -79,6 +79,8 @@ struct mnt_id_req;
 struct ns_id_req;
 struct xattr_args;
 struct file_attr;
+struct mq_msg_attrs;
+struct mq_mmsg_attrs;
 
 #include <linux/types.h>
 #include <linux/aio_abi.h>
@@ -739,7 +741,14 @@ asmlinkage long sys_sysinfo(struct sysinfo __user *info);
 asmlinkage long sys_mq_open(const char __user *name, int oflag, umode_t mode, struct mq_attr __user *attr);
 asmlinkage long sys_mq_unlink(const char __user *name);
 asmlinkage long sys_mq_timedsend(mqd_t mqdes, const char __user *msg_ptr, size_t msg_len, unsigned int msg_prio, const struct __kernel_timespec __user *abs_timeout);
+asmlinkage long sys_mq_sendmmsg(mqd_t mqdes, struct mq_mmsg_attrs __user *attrs,
+				unsigned int attrs_len, unsigned long start_index,
+				const struct __kernel_timespec __user *abs_timeout);
 asmlinkage long sys_mq_timedreceive(mqd_t mqdes, char __user *msg_ptr, size_t msg_len, unsigned int __user *msg_prio, const struct __kernel_timespec __user *abs_timeout);
+asmlinkage long sys_mq_recvmmsg(mqd_t mqdes, struct mq_mmsg_attrs __user *attrs,
+				unsigned int attrs_len, unsigned int flags,
+				unsigned long start_index,
+				const struct __kernel_timespec __user *abs_timeout);
 asmlinkage long sys_mq_notify(mqd_t mqdes, const struct sigevent __user *notification);
 asmlinkage long sys_mq_getsetattr(mqd_t mqdes, const struct mq_attr __user *mqstat, struct mq_attr __user *omqstat);
 asmlinkage long sys_mq_timedreceive_time32(mqd_t mqdes,
diff --git a/include/uapi/asm-generic/unistd.h b/include/uapi/asm-generic/unistd.h
index a627acc8fb5f..1d06486d3aa5 100644
--- a/include/uapi/asm-generic/unistd.h
+++ b/include/uapi/asm-generic/unistd.h
@@ -863,9 +863,14 @@ __SYSCALL(__NR_listns, sys_listns)
 #define __NR_rseq_slice_yield 471
 __SYSCALL(__NR_rseq_slice_yield, sys_rseq_slice_yield)
 
-#undef __NR_syscalls
-#define __NR_syscalls 472
+#define __NR_mq_recvmmsg 472
+__SC_COMP(__NR_mq_recvmmsg, sys_mq_recvmmsg, compat_sys_mq_recvmmsg)
+
+#define __NR_mq_sendmmsg 473
+__SC_COMP(__NR_mq_sendmmsg, sys_mq_sendmmsg, compat_sys_mq_sendmmsg)
 
+#undef __NR_syscalls
+#define __NR_syscalls 474
 /*
  * 32 bit systems traditionally used different
  * syscalls for off_t and loff_t arguments, while
diff --git a/include/uapi/linux/mqueue.h b/include/uapi/linux/mqueue.h
index b516b66840ad..6ce21e41eb18 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 <linux/compat.h>
 
 #define MQ_PRIO_MAX 	32768
 /* per-uid limit of kernel memory used by mqueue, in bytes */
@@ -33,6 +34,30 @@ struct mq_attr {
 	__kernel_long_t	__reserved[4];	/* ignored for input, zeroed for output */
 };
 
+struct mq_msg_attrs {
+	__kernel_size_t msg_len;
+	unsigned int __user *msg_prio;
+	void __user *msg_ptr;
+};
+
+struct mq_mmsg_attrs {
+	struct iovec __user *msg_attrs_vec;
+	__kernel_size_t vlen;
+	int __user  *ret;
+};
+
+struct compat_msg_attrs {
+	compat_size_t msg_len;
+	compat_uptr_t __user msg_prio;
+	compat_uptr_t __user msg_ptr;
+};
+
+struct compat_mq_mmsg_attrs {
+	compat_uptr_t __user msg_attrs_vec;
+	compat_size_t vlen;
+	compat_uptr_t __user ret;
+};
+
 /*
  * SIGEV_THREAD implementation:
  * SIGEV_THREAD must be implemented in user space. If SIGEV_THREAD is passed
diff --git a/ipc/mqueue.c b/ipc/mqueue.c
index 4798b375972b..a19e24f7b3aa 100644
--- a/ipc/mqueue.c
+++ b/ipc/mqueue.c
@@ -11,7 +11,8 @@
  *
  * Audit:                   George Wilson           (ltcgcw@us.ibm.com)
  */
-
+#include "linux/build_bug.h"
+#include "linux/printk.h"
 #include <linux/capability.h>
 #include <linux/init.h>
 #include <linux/pagemap.h>
@@ -39,7 +40,10 @@
 #include <linux/sched/signal.h>
 #include <linux/sched/user.h>
 
+#include <linux/uio.h>
+#include <linux/uaccess.h>
 #include <net/sock.h>
+#include <linux/rbtree_augmented.h>
 #include "util.h"
 
 struct mqueue_fs_context {
@@ -54,6 +58,16 @@ 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)
+
+/* List of all mq_mmsg_attrs "versions". */
+#define MQ_MMSG_ATTRS_SIZE_VER0	24
+#define MQ_MMSG_ATTRS_SIZE_LATEST MQ_MMSG_ATTRS_SIZE_VER0
+#define COMPAT_MQ_MMSG_ATTRS_SIZE_VER0	12
+#define COMPAT_MQ_MMSG_ATTRS_SIZE_LATEST COMPAT_MQ_MMSG_ATTRS_SIZE_VER0
+
 #define STATE_NONE	0
 #define STATE_READY	1
 
@@ -61,6 +75,8 @@ struct posix_msg_tree_node {
 	struct rb_node		rb_node;
 	struct list_head	msg_list;
 	int			priority;
+	unsigned int msg_count; /* Total messages at exactly this priority */
+	unsigned int subtree_msg_count; /* sum of messages in this node and all descendants */
 };
 
 /*
@@ -186,7 +202,53 @@ static struct ipc_namespace *get_ns_from_inode(struct inode *inode)
 	return ns;
 }
 
-/* Auxiliary functions to manipulate messages' list */
+static inline unsigned int get_subtree_count(struct rb_node *node)
+{
+	if (!node)
+		return 0;
+	return rb_entry(node, struct posix_msg_tree_node, rb_node)->subtree_msg_count;
+}
+
+static void msg_tree_propagate_subtree_msg_count(struct rb_node *node, struct rb_node *stop)
+{
+	while (node != stop) {
+		struct posix_msg_tree_node *leaf = rb_entry(node, struct posix_msg_tree_node,
+							    rb_node);
+		unsigned int new_count = leaf->msg_count +
+					 get_subtree_count(node->rb_left) +
+					 get_subtree_count(node->rb_right);
+		if (leaf->subtree_msg_count == new_count)
+			break;
+		leaf->subtree_msg_count = new_count;
+		node = rb_parent(node);
+	}
+}
+
+static void msg_tree_copy_subtree_msg_count(struct rb_node *old, struct rb_node *new)
+{
+	struct posix_msg_tree_node *old_leaf = rb_entry(old, struct posix_msg_tree_node, rb_node);
+	struct posix_msg_tree_node *new_leaf = rb_entry(new, struct posix_msg_tree_node, rb_node);
+
+	new_leaf->subtree_msg_count = old_leaf->subtree_msg_count;
+}
+
+static void msg_tree_rotate_subtree_msg_count(struct rb_node *old, struct rb_node *new)
+{
+	struct posix_msg_tree_node *old_leaf = rb_entry(old, struct posix_msg_tree_node, rb_node);
+	struct posix_msg_tree_node *new_leaf = rb_entry(new, struct posix_msg_tree_node, rb_node);
+
+	new_leaf->subtree_msg_count = old_leaf->subtree_msg_count;
+	old_leaf->subtree_msg_count = old_leaf->msg_count +
+				 get_subtree_count(old->rb_left) +
+				 get_subtree_count(old->rb_right);
+}
+
+static const struct rb_augment_callbacks msg_tree_callbacks = {
+	.propagate = msg_tree_propagate_subtree_msg_count,
+	.copy = msg_tree_copy_subtree_msg_count,
+	.rotate = msg_tree_rotate_subtree_msg_count
+};
+
 static int msg_insert(struct msg_msg *msg, struct mqueue_inode_info *info)
 {
 	struct rb_node **p, *parent = NULL;
@@ -216,13 +278,20 @@ static int msg_insert(struct msg_msg *msg, struct mqueue_inode_info *info)
 		INIT_LIST_HEAD(&leaf->msg_list);
 	}
 	leaf->priority = msg->m_type;
+	leaf->msg_count = 1;
+	leaf->subtree_msg_count = 1;
 
 	if (rightmost)
 		info->msg_tree_rightmost = &leaf->rb_node;
 
 	rb_link_node(&leaf->rb_node, parent, p);
-	rb_insert_color(&leaf->rb_node, &info->msg_tree);
+	msg_tree_propagate_subtree_msg_count(parent, NULL);
+	rb_insert_augmented(&leaf->rb_node, &info->msg_tree, &msg_tree_callbacks);
+	goto common_insert;
 insert_msg:
+	leaf->msg_count++;
+	msg_tree_propagate_subtree_msg_count(&leaf->rb_node, NULL);
+common_insert:
 	info->attr.mq_curmsgs++;
 	info->qsize += msg->m_ts;
 	list_add_tail(&msg->m_list, &leaf->msg_list);
@@ -236,8 +305,7 @@ static inline void msg_tree_erase(struct posix_msg_tree_node *leaf,
 
 	if (info->msg_tree_rightmost == node)
 		info->msg_tree_rightmost = rb_prev(node);
-
-	rb_erase(node, &info->msg_tree);
+	rb_erase_augmented(node, &info->msg_tree, &msg_tree_callbacks);
 	if (info->node_cache)
 		kfree(leaf);
 	else
@@ -277,8 +345,11 @@ static inline struct msg_msg *msg_get(struct mqueue_inode_info *info)
 		msg = list_first_entry(&leaf->msg_list,
 				       struct msg_msg, m_list);
 		list_del(&msg->m_list);
+		leaf->msg_count--;
 		if (list_empty(&leaf->msg_list)) {
 			msg_tree_erase(leaf, info);
+		} else {
+			msg_tree_propagate_subtree_msg_count(&leaf->rb_node, NULL);
 		}
 	}
 	info->attr.mq_curmsgs--;
@@ -765,7 +836,6 @@ static struct ext_wait_queue *wq_get_first_waiter(
 	return list_entry(ptr, struct ext_wait_queue, list);
 }
 
-
 static inline void set_cookie(struct sk_buff *skb, char code)
 {
 	((char *)skb->data)[NOTIFY_COOKIE_LEN-1] = code;
@@ -1034,29 +1104,27 @@ static inline void pipelined_receive(struct wake_q_head *wake_q,
 	__pipelined_op(wake_q, info, sender);
 }
 
-static int do_mq_timedsend(mqd_t mqdes, const char __user *u_msg_ptr,
-		size_t msg_len, unsigned int msg_prio,
-		struct timespec64 *ts)
+static ssize_t do_mq_sendmsg(mqd_t mqdes, const char __user *u_msg_ptr, size_t msg_len,
+			     unsigned int msg_prio, ktime_t *timeout)
 {
+	ssize_t ret = 0;
 	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);
+	struct timespec64 ts, *abs_timeout = NULL;
 
+	DEFINE_WAKE_Q(wake_q);
 	if (unlikely(msg_prio >= (unsigned long) MQ_PRIO_MAX))
 		return -EINVAL;
 
-	if (ts) {
-		expires = timespec64_to_ktime(*ts);
-		timeout = &expires;
+	if (timeout) {
+		ts = ktime_to_timespec64(*timeout);
+		abs_timeout = &ts;
 	}
-
-	audit_mq_sendrecv(mqdes, msg_len, msg_prio, ts);
+	audit_mq_sendrecv(mqdes, msg_len, msg_prio, abs_timeout);
 
 	CLASS(fd, f)(mqdes);
 	if (fd_empty(f))
@@ -1139,24 +1207,37 @@ static int do_mq_timedsend(mqd_t mqdes, const char __user *u_msg_ptr,
 	return ret;
 }
 
-static int do_mq_timedreceive(mqd_t mqdes, char __user *u_msg_ptr,
-		size_t msg_len, unsigned int __user *u_msg_prio,
-		struct timespec64 *ts)
+static ssize_t do_mq_timedsend(mqd_t mqdes, const char __user *u_msg_ptr,
+			       size_t msg_len, unsigned int msg_prio,
+			       struct timespec64 *ts)
+{
+	ktime_t expires, *timeout = NULL;
+
+	if (ts) {
+		expires = timespec64_to_ktime(*ts);
+		timeout = &expires;
+	}
+
+	return do_mq_sendmsg(mqdes, u_msg_ptr, msg_len, msg_prio, timeout);
+}
+
+static ssize_t do_mq_recvmsg(mqd_t mqdes, char __user *u_msg_ptr, size_t msg_len,
+			     unsigned int __user *u_msg_prio, ktime_t *timeout)
 {
 	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;
+	struct timespec64 ts, *abs_timeout = NULL;
 
-	if (ts) {
-		expires = timespec64_to_ktime(*ts);
-		timeout = &expires;
+	if (timeout) {
+		ts = ktime_to_timespec64(*timeout);
+		abs_timeout = &ts;
 	}
 
-	audit_mq_sendrecv(mqdes, msg_len, 0, ts);
+	audit_mq_sendrecv(mqdes, msg_len, 0, abs_timeout);
 
 	CLASS(fd, f)(mqdes);
 	if (fd_empty(f))
@@ -1222,7 +1303,7 @@ static int do_mq_timedreceive(mqd_t mqdes, char __user *u_msg_ptr,
 		ret = msg_ptr->m_ts;
 
 		if ((u_msg_prio && put_user(msg_ptr->m_type, u_msg_prio)) ||
-			store_msg(u_msg_ptr, msg_ptr, msg_ptr->m_ts)) {
+		     store_msg(u_msg_ptr, msg_ptr, msg_ptr->m_ts)) {
 			ret = -EFAULT;
 		}
 		free_msg(msg_ptr);
@@ -1230,6 +1311,366 @@ static int do_mq_timedreceive(mqd_t mqdes, char __user *u_msg_ptr,
 	return ret;
 }
 
+static ssize_t do_mq_timedreceive(mqd_t mqdes, char __user *u_msg_ptr, size_t msg_len,
+				  unsigned int __user *u_msg_prio, struct timespec64 *ts)
+{
+	ktime_t expires, *timeout = NULL;
+
+	if (ts) {
+		expires = timespec64_to_ktime(*ts);
+		timeout = &expires;
+	}
+
+	return do_mq_recvmsg(mqdes, u_msg_ptr, msg_len, u_msg_prio, timeout);
+}
+
+static struct msg_msg *mq_peek_index(struct mqueue_inode_info *info, unsigned long index)
+{
+	struct rb_node *node;
+	struct posix_msg_tree_node *leaf;
+	struct msg_msg *msg;
+	unsigned int right_count;
+	unsigned int offset;
+	unsigned int i = 0;
+
+	node = info->msg_tree.rb_node;
+	while (node) {
+		leaf = rb_entry(node, struct posix_msg_tree_node, rb_node);
+		right_count = get_subtree_count(node->rb_right);
+
+		if (index < right_count) {
+			node = node->rb_right;
+		} else if (index < (right_count + leaf->msg_count)) {
+			/* Target is at this priority level */
+			offset = index - right_count;
+			list_for_each_entry(msg, &leaf->msg_list, m_list) {
+				if (i == offset)
+					return msg;
+				i++;
+			}
+			WARN_ON_ONCE(i != offset);
+			break;
+		} else {
+			index -= (right_count + leaf->msg_count);
+			node = node->rb_left;
+		}
+	}
+
+	return NULL;
+}
+
+static ssize_t do_mq_recvmsg2(mqd_t mqdes, struct mq_msg_attrs *args, unsigned int flags,
+			      unsigned long index, 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;
+	struct timespec64 ts, *abs_timeout = NULL;
+
+	if (timeout) {
+		ts = ktime_to_timespec64(*timeout);
+		abs_timeout = &ts;
+	}
+	if (flags & MQ_PEEK) {
+		audit_mq_sendrecv(mqdes, args->msg_len, 0, abs_timeout);
+		CLASS(fd, f)(mqdes);
+		if (fd_empty(f))
+			return -EBADF;
+
+		inode = file_inode(fd_file(f));
+		if (unlikely(fd_file(f)->f_op != &mqueue_file_operations))
+			return -EBADF;
+
+		info = MQUEUE_I(inode);
+		audit_file(fd_file(f));
+		if (unlikely(!(fd_file(f)->f_mode & FMODE_READ)))
+			return -EBADF;
+
+		if (unlikely(args->msg_len < info->attr.mq_msgsize))
+			return -EMSGSIZE;
+
+		if (index >= (unsigned long)info->attr.mq_maxmsg)
+			return -EINVAL;
+
+		spin_lock(&info->lock);
+
+		if (info->attr.mq_curmsgs == 0) {
+			spin_unlock(&info->lock);
+			return -EAGAIN;
+		}
+		msg_ptr = mq_peek_index(info, index);
+		if (!msg_ptr) {
+			spin_unlock(&info->lock);
+			return -ENODATA;
+		}
+		k_m_type = msg_ptr->m_type;
+		k_m_ts = msg_ptr->m_ts;
+
+		spin_unlock(&info->lock);
+
+		k_msg_buffer = alloc_msg(k_m_ts);
+
+		if (!k_msg_buffer)
+			return -ENOMEM;
+		ret = security_msg_msg_alloc(k_msg_buffer);
+		if (ret) {
+			free_msg(k_msg_buffer);
+			return ret;
+		}
+
+	/*
+	 * Two spin locks are necessary here. We are avoiding atomic memory
+	 * allocation and premature allocation before confirming
+	 * a message actually exists to peek and retrieving required buffer size
+	 * when first lock was taken.
+	 */
+		spin_lock(&info->lock);
+
+		msg_ptr = mq_peek_index(info, index);
+		if (!msg_ptr || msg_ptr->m_type != k_m_type ||
+		     msg_ptr->m_ts != k_m_ts) {
+			spin_unlock(&info->lock);
+			free_msg(k_msg_buffer);
+			return -EAGAIN;
+		}
+		msg_ptr = copy_msg(msg_ptr, k_msg_buffer, k_m_ts);
+		if (IS_ERR(msg_ptr)) {
+			spin_unlock(&info->lock);
+			free_msg(k_msg_buffer);
+			return PTR_ERR(msg_ptr);
+		}
+		spin_unlock(&info->lock);
+
+		ret = k_msg_buffer->m_ts;
+		if (args->msg_prio && put_user(k_m_type, args->msg_prio)) {
+			free_msg(k_msg_buffer);
+			return -EFAULT;
+		}
+		if (store_msg((char *)args->msg_ptr, k_msg_buffer, k_m_ts)) {
+			free_msg(k_msg_buffer);
+			return -EFAULT;
+		}
+		free_msg(k_msg_buffer);
+			return ret;
+	}
+	if (flags & MQ_RECV) {
+		return do_mq_recvmsg(mqdes, (char *)args->msg_ptr, args->msg_len,
+				args->msg_prio, 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)
+{
+	BUILD_BUG_ON(sizeof(*attrs) < MQ_MMSG_ATTRS_SIZE_VER0);
+	BUILD_BUG_ON(sizeof(*attrs) != MQ_MMSG_ATTRS_SIZE_LATEST);
+
+	if (unlikely(attrs_len < MQ_MMSG_ATTRS_SIZE_VER0))
+		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;
+
+	BUILD_BUG_ON(sizeof(*attrs) < MQ_MMSG_ATTRS_SIZE_VER0);
+	BUILD_BUG_ON(sizeof(*attrs) != MQ_MMSG_ATTRS_SIZE_LATEST);
+	BUILD_BUG_ON(sizeof(v) != COMPAT_MQ_MMSG_ATTRS_SIZE_VER0);
+
+	if (unlikely(attrs_len < COMPAT_MQ_MMSG_ATTRS_SIZE_VER0))
+		return -EINVAL;
+	if (unlikely(attrs_len > PAGE_SIZE))
+		return -E2BIG;
+	err = copy_struct_from_user(&v, sizeof(v), uattrs, attrs_len);
+
+	if (err)
+		return err;
+	attrs->msg_attrs_vec = (struct iovec __user *)compat_ptr((unsigned long)v.msg_attrs_vec);
+	attrs->ret = (int *)compat_ptr(v.ret);
+	attrs->vlen = v.vlen;
+	return 0;
+}
+
+static int mq_mmsg_copy_compat_msg_attr(struct mq_msg_attrs *attr,
+					const struct iovec *desc_iov)
+{
+		struct compat_msg_attrs v = {};
+
+		if (desc_iov->iov_len != sizeof(v))
+			return -EINVAL;
+		if (copy_from_user(&v, desc_iov->iov_base, sizeof(v)))
+			return -EFAULT;
+
+		attr->msg_len = v.msg_len;
+		attr->msg_prio = (unsigned int *)compat_ptr(v.msg_prio);
+		attr->msg_ptr = compat_ptr(v.msg_ptr);
+		return 0;
+	}
+
+#endif
+
+static int mq_mmsg_copy_msg_attr(struct mq_msg_attrs *attr,
+				 const struct iovec *desc_iov, bool compat)
+{
+	if (compat)
+		return mq_mmsg_copy_compat_msg_attr(attr, desc_iov);
+	if (desc_iov->iov_len != sizeof(*attr))
+		return -EINVAL;
+	if (copy_from_user(attr, desc_iov->iov_base, sizeof(*attr)))
+		return -EFAULT;
+	return 0;
+}
+
+static inline long mq_mmsg_done_or_error(unsigned int done, int ret)
+{
+	if (ret < 0 && done)
+		return done;
+	return ret;
+}
+
+static ssize_t do_mq_recvmmsg(mqd_t mqdes, const struct mq_mmsg_attrs *attrs,
+			      unsigned int flags, unsigned long start_idx,
+			      struct timespec64 *ts, bool compat)
+{
+	struct iov_iter iter;
+	struct iovec outer_iovstack[UIO_FASTIOV], *free_iov = outer_iovstack;
+	const struct iovec *msg_iov;
+	ktime_t batch_deadline, *timeout = NULL;
+	ssize_t ret = 0;
+	ssize_t batch_success = 0;
+	unsigned long index;
+	unsigned int i;
+
+	if (flags & ~MQ_VALID_FLAGS)
+		return -EINVAL;
+	if ((flags & MQ_RECV) && (flags & MQ_PEEK))
+		return -EINVAL;
+	if (start_idx > attrs->vlen)
+		return -EINVAL;
+	if (!attrs->vlen || attrs->vlen > UIO_MAXIOV)
+		return -EINVAL;
+
+	ret = __import_iovec(ITER_DEST,
+			     attrs->msg_attrs_vec, attrs->vlen,
+			     ARRAY_SIZE(outer_iovstack), &free_iov, &iter,
+			     compat);
+	if (ret < 0)
+		return ret;
+	msg_iov = iter_iov(&iter);
+
+	/* One absolute deadline for the whole batch. */
+	if (ts) {
+		batch_deadline = timespec64_to_ktime(*ts);
+		timeout = &batch_deadline;
+	}
+	for (i = start_idx; i < attrs->vlen; i++) {
+		struct mq_msg_attrs desc = {};
+
+		ret = mq_mmsg_copy_msg_attr(&desc, &msg_iov[i], compat);
+		if (ret < 0)
+			break;
+
+		index = (flags & MQ_PEEK) ? i : 0;
+		ret = do_mq_recvmsg2(mqdes, &desc, flags, index, 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, 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 +1685,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)
@@ -1258,6 +1720,27 @@ SYSCALL_DEFINE5(mq_timedreceive, mqd_t, mqdes, char __user *, u_msg_ptr,
 	return do_mq_timedreceive(mqdes, u_msg_ptr, msg_len, u_msg_prio, p);
 }
 
+SYSCALL_DEFINE6(mq_recvmmsg, mqd_t, mqdes, struct mq_mmsg_attrs __user *, attrs,
+		unsigned int, attrs_len, unsigned int, flags, unsigned long, start_idx,
+		const struct __kernel_timespec __user *, u_abs_timeout)
+{
+	struct mq_mmsg_attrs kattrs = {};
+	struct timespec64 ts, *p = NULL;
+	int ret;
+
+	ret = mq_mmsg_copy_attrs_from_user(&kattrs, attrs, attrs_len);
+	if (ret)
+		return ret;
+	if (u_abs_timeout) {
+		int res = prepare_timeout(u_abs_timeout, &ts);
+
+		if (res)
+			return res;
+		p = &ts;
+	}
+	return do_mq_recvmmsg(mqdes, &kattrs, flags, start_idx, p, false);
+}
+
 /*
  * Notes: the case when user wants us to deregister (with NULL as pointer)
  * and he isn't currently owner of notification, will be silently discarded.
@@ -1460,7 +1943,7 @@ struct compat_mq_attr {
 };
 
 static inline int get_compat_mq_attr(struct mq_attr *attr,
-			const struct compat_mq_attr __user *uattr)
+				     const struct compat_mq_attr __user *uattr)
 {
 	struct compat_mq_attr v;
 
@@ -1476,7 +1959,7 @@ static inline int get_compat_mq_attr(struct mq_attr *attr,
 }
 
 static inline int put_compat_mq_attr(const struct mq_attr *attr,
-			struct compat_mq_attr __user *uattr)
+				     struct compat_mq_attr __user *uattr)
 {
 	struct compat_mq_attr v;
 
@@ -1491,8 +1974,8 @@ static inline int put_compat_mq_attr(const struct mq_attr *attr,
 }
 
 COMPAT_SYSCALL_DEFINE4(mq_open, const char __user *, u_name,
-		       int, oflag, compat_mode_t, mode,
-		       struct compat_mq_attr __user *, u_attr)
+		      int, oflag, compat_mode_t, mode,
+		      struct compat_mq_attr __user *, u_attr)
 {
 	struct mq_attr attr, *p = NULL;
 	if (u_attr && oflag & O_CREAT) {
@@ -1504,7 +1987,7 @@ COMPAT_SYSCALL_DEFINE4(mq_open, const char __user *, u_name,
 }
 
 COMPAT_SYSCALL_DEFINE2(mq_notify, mqd_t, mqdes,
-		       const struct compat_sigevent __user *, u_notification)
+		      const struct compat_sigevent __user *, u_notification)
 {
 	struct sigevent n, *p = NULL;
 	if (u_notification) {
@@ -1541,6 +2024,56 @@ COMPAT_SYSCALL_DEFINE3(mq_getsetattr, mqd_t, mqdes,
 		return -EFAULT;
 	return 0;
 }
+
+COMPAT_SYSCALL_DEFINE5(mq_sendmmsg, mqd_t, mqdes, struct compat_mq_mmsg_attrs __user *, attrs,
+		       unsigned int, attrs_len, unsigned long, start_idx,
+		       const struct __kernel_timespec __user *, u_abs_timeout)
+{
+	struct mq_mmsg_attrs kattrs = {};
+	struct timespec64 ts, *p = NULL;
+	struct iovec msg_atrrs_vec = {};
+	int ret;
+
+	kattrs.msg_attrs_vec = &msg_atrrs_vec;
+	ret = mq_mmsg_copy_compat_attrs(&kattrs, attrs, attrs_len);
+	if (ret)
+		return ret;
+
+	if (u_abs_timeout) {
+		int res = prepare_timeout(u_abs_timeout, &ts);
+
+		if (res)
+			return res;
+		p = &ts;
+	}
+
+	return do_mq_sendmmsg(mqdes, &kattrs, start_idx, p, true);
+}
+
+COMPAT_SYSCALL_DEFINE6(mq_recvmmsg, mqd_t, mqdes, struct compat_mq_mmsg_attrs __user *, attrs,
+		       unsigned int, attrs_len, unsigned int, flags, unsigned long, start_idx,
+		       const struct __kernel_timespec __user *, u_abs_timeout)
+{
+	struct mq_mmsg_attrs kattrs = {};
+	struct timespec64 ts, *p = NULL;
+	struct iovec msg_atrrs_vec = {};
+	int ret;
+
+	kattrs.msg_attrs_vec = &msg_atrrs_vec;
+	ret = mq_mmsg_copy_compat_attrs(&kattrs, attrs, attrs_len);
+	if (ret)
+		return ret;
+
+	if (u_abs_timeout) {
+		int res = prepare_timeout(u_abs_timeout, &ts);
+
+		if (res)
+			return res;
+		p = &ts;
+	}
+
+	return do_mq_recvmmsg(mqdes, &kattrs, flags, start_idx, p, true);
+}
 #endif
 
 #ifdef CONFIG_COMPAT_32BIT_TIME
diff --git a/ipc/msg.c b/ipc/msg.c
index 62996b97f0ac..53c1632dc22a 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, min_t(size_t, bufsz, ns->msg_ctlmax));
 				goto out_unlock0;
 			}
 
diff --git a/ipc/msgutil.c b/ipc/msgutil.c
index e28f0cecb2ec..a022fd36a356 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);
+	chunk = min(len, DATALEN_MSG);
 
-	for (dst_pseg = dst->next, src_pseg = src->next;
-	     src_pseg != NULL;
-	     dst_pseg = dst_pseg->next, src_pseg = src_pseg->next) {
+	memcpy(dst + 1, src + 1, chunk);
+	remaining = len - chunk;
+	src_seg = src->next;
+	dst_seg = dst->next;
 
-		len -= alen;
-		alen = min(len, DATALEN_SEG);
-		memcpy(dst_pseg + 1, src_pseg + 1, alen);
+	while (remaining > 0 && src_seg && dst_seg) {
+		chunk = min(remaining, DATALEN_SEG);
+		memcpy(dst_seg + 1, src_seg + 1, chunk);
+		remaining -= chunk;
+		src_seg = src_seg->next;
+		dst_seg = dst_seg->next;
 	}
-
+	if (remaining != 0)
+		return ERR_PTR(-EINVAL);
 	dst->m_type = src->m_type;
-	dst->m_ts = src->m_ts;
-
+	dst->m_ts = len;
 	return dst;
 }
-#else
-struct msg_msg *copy_msg(struct msg_msg *src, struct msg_msg *dst)
-{
-	return ERR_PTR(-ENOSYS);
-}
-#endif
+
 int store_msg(void __user *dest, struct msg_msg *msg, size_t len)
 {
 	size_t alen;
diff --git a/ipc/util.h b/ipc/util.h
index a55d6cebe6d3..374abeee79b3 100644
--- a/ipc/util.h
+++ b/ipc/util.h
@@ -197,8 +197,9 @@ int ipc_parse_version(int *cmd);
 
 extern void free_msg(struct msg_msg *msg);
 extern struct msg_msg *load_msg(const void __user *src, size_t len);
-extern struct msg_msg *copy_msg(struct msg_msg *src, struct msg_msg *dst);
+extern struct msg_msg *copy_msg(struct msg_msg *src, struct msg_msg *dst, size_t len);
 extern int store_msg(void __user *dest, struct msg_msg *msg, size_t len);
+extern struct msg_msg *alloc_msg(size_t len);
 
 static inline int ipc_checkid(struct kern_ipc_perm *ipcp, int id)
 {
diff --git a/kernel/sys_ni.c b/kernel/sys_ni.c
index add3032da16f..8219d76c72a0 100644
--- a/kernel/sys_ni.c
+++ b/kernel/sys_ni.c
@@ -392,5 +392,11 @@ COND_SYSCALL(setuid16);
 COND_SYSCALL(rseq);
 COND_SYSCALL(rseq_slice_yield);
 
+/* ipc */
+COND_SYSCALL(mq_recvmmsg);
+COND_SYSCALL_COMPAT(mq_recvmmsg);
+COND_SYSCALL(mq_sendmmsg);
+COND_SYSCALL_COMPAT(mq_sendmmsg);
+
 COND_SYSCALL(uretprobe);
 COND_SYSCALL(uprobe);
-- 
2.43.0


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH v4 2/4]  IPC: Added system call entry in all of most common  architectures
  2026-09-24 15:34 [PATCH v4 0/4] Add two new system call mq_recvmmsg() and mq_sendmmsg() to posix ipc mqueue mathura kumar
  2026-09-24 15:34 ` [PATCH v4 1/4] IPC: Added two new system call mq_recvmmsg() and mq_sendmmsg() mathura kumar
@ 2026-09-24 15:35 ` mathura kumar
  2026-09-24 15:35 ` [PATCH v4 3/4] IPC: Added system call entry in performance tool mathura kumar
  2026-09-24 15:35 ` [PATCH v4 4/4] Test: Added self-testing and documentation mathura kumar
  3 siblings, 0 replies; 5+ messages in thread
From: mathura kumar @ 2026-09-24 15:35 UTC (permalink / raw)
  To: brauner; +Cc: linux-kernel, linux-arch, corbet, mathura.kumar.tech

Wire up the new syscall numbers for all common architectures
in their respective syscall tables.

Signed-off-by: mathura kumar <mathura.kumar.tech@gmail.com>
---
 arch/alpha/kernel/syscalls/syscall.tbl      | 2 ++
 arch/arm/tools/syscall.tbl                  | 2 ++
 arch/arm64/tools/syscall_32.tbl             | 3 +++
 arch/m68k/kernel/syscalls/syscall.tbl       | 2 ++
 arch/microblaze/kernel/syscalls/syscall.tbl | 2 ++
 arch/mips/kernel/syscalls/syscall_n32.tbl   | 2 ++
 arch/mips/kernel/syscalls/syscall_n64.tbl   | 3 +++
 arch/mips/kernel/syscalls/syscall_o32.tbl   | 2 ++
 arch/parisc/kernel/syscalls/syscall.tbl     | 2 ++
 arch/powerpc/kernel/syscalls/syscall.tbl    | 2 ++
 arch/s390/kernel/syscalls/syscall.tbl       | 3 +++
 arch/sh/kernel/syscalls/syscall.tbl         | 2 ++
 arch/sparc/kernel/syscalls/syscall.tbl      | 3 +++
 arch/x86/entry/syscalls/syscall_32.tbl      | 2 ++
 arch/x86/entry/syscalls/syscall_64.tbl      | 3 ++-
 arch/xtensa/kernel/syscalls/syscall.tbl     | 2 ++
 scripts/syscall.tbl                         | 2 ++
 17 files changed, 38 insertions(+), 1 deletion(-)

diff --git a/arch/alpha/kernel/syscalls/syscall.tbl b/arch/alpha/kernel/syscalls/syscall.tbl
index f31b7afffc34..d3f73725239b 100644
--- a/arch/alpha/kernel/syscalls/syscall.tbl
+++ b/arch/alpha/kernel/syscalls/syscall.tbl
@@ -511,3 +511,5 @@
 579	common	file_setattr			sys_file_setattr
 580	common	listns				sys_listns
 581	common	rseq_slice_yield		sys_rseq_slice_yield
+582	common	mq_recvmmsg			sys_mq_recvmmsg
+583	common	mq_sendmmsg			sys_mq_sendmmsg
diff --git a/arch/arm/tools/syscall.tbl b/arch/arm/tools/syscall.tbl
index 94351e22bfcf..ad9080eb5800 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..7d758c8de10f 100644
--- a/arch/arm64/tools/syscall_32.tbl
+++ b/arch/arm64/tools/syscall_32.tbl
@@ -483,3 +483,6 @@
 469	common	file_setattr			sys_file_setattr
 470	common	listns				sys_listns
 471	common	rseq_slice_yield		sys_rseq_slice_yield
+472	common	mq_recvmmsg			sys_mq_recvmmsg		compat_sys_mq_recvmmsg
+473	common	mq_sendmmsg			sys_mq_sendmmsg		compat_sys_mq_sendmmsg
+
diff --git a/arch/m68k/kernel/syscalls/syscall.tbl b/arch/m68k/kernel/syscalls/syscall.tbl
index 248934257101..42e277f7f1bc 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..8f92da4e6c86 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
+472	common	mq_recvmmsg			sys_mq_recvmmsg
+473	common	mq_sendmmsg			sys_mq_sendmmsg
diff --git a/arch/mips/kernel/syscalls/syscall_n32.tbl b/arch/mips/kernel/syscalls/syscall_n32.tbl
index 7430714e2b8f..03099ba20103 100644
--- a/arch/mips/kernel/syscalls/syscall_n32.tbl
+++ b/arch/mips/kernel/syscalls/syscall_n32.tbl
@@ -410,3 +410,5 @@
 469	n32	file_setattr			sys_file_setattr
 470	n32	listns				sys_listns
 471	n32	rseq_slice_yield		sys_rseq_slice_yield
+472	n32	mq_recvmmsg			compat_sys_mq_recvmmsg
+473	n32	mq_sendmmsg			compat_sys_mq_sendmmsg
diff --git a/arch/mips/kernel/syscalls/syscall_n64.tbl b/arch/mips/kernel/syscalls/syscall_n64.tbl
index 630aab9e5425..49b52129b110 100644
--- a/arch/mips/kernel/syscalls/syscall_n64.tbl
+++ b/arch/mips/kernel/syscalls/syscall_n64.tbl
@@ -386,3 +386,6 @@
 469	n64	file_setattr			sys_file_setattr
 470	n64	listns				sys_listns
 471	n64	rseq_slice_yield		sys_rseq_slice_yield
+472	n64	mq_recvmmsg			sys_mq_recvmmsg
+473	n64	mq_sendmmsg			sys_mq_sendmmsg
+
diff --git a/arch/mips/kernel/syscalls/syscall_o32.tbl b/arch/mips/kernel/syscalls/syscall_o32.tbl
index 128653112284..c26fa87ae06e 100644
--- a/arch/mips/kernel/syscalls/syscall_o32.tbl
+++ b/arch/mips/kernel/syscalls/syscall_o32.tbl
@@ -459,3 +459,5 @@
 469	o32	file_setattr			sys_file_setattr
 470	o32	listns				sys_listns
 471	o32	rseq_slice_yield		sys_rseq_slice_yield
+472	o32	mq_recvmmsg			sys_mq_recvmmsg		compat_sys_mq_recvmmsg
+473	o32	mq_sendmmsg			sys_mq_sendmmsg		compat_sys_mq_sendmmsg
diff --git a/arch/parisc/kernel/syscalls/syscall.tbl b/arch/parisc/kernel/syscalls/syscall.tbl
index c6331dad9461..fd3c4fde3298 100644
--- a/arch/parisc/kernel/syscalls/syscall.tbl
+++ b/arch/parisc/kernel/syscalls/syscall.tbl
@@ -470,3 +470,5 @@
 469	common	file_setattr			sys_file_setattr
 470	common	listns				sys_listns
 471	common	rseq_slice_yield		sys_rseq_slice_yield
+472	common	mq_recvmmsg			sys_mq_recvmmsg		compat_sys_mq_recvmmsg
+473	common	mq_sendmmsg			sys_mq_sendmmsg		compat_sys_mq_sendmmsg
diff --git a/arch/powerpc/kernel/syscalls/syscall.tbl b/arch/powerpc/kernel/syscalls/syscall.tbl
index 4fcc7c58a105..a84355ada041 100644
--- a/arch/powerpc/kernel/syscalls/syscall.tbl
+++ b/arch/powerpc/kernel/syscalls/syscall.tbl
@@ -562,3 +562,5 @@
 469	common	file_setattr			sys_file_setattr
 470	common	listns				sys_listns
 471	nospu	rseq_slice_yield		sys_rseq_slice_yield
+472	common	mq_recvmmsg			sys_mq_recvmmsg		compat_sys_mq_recvmmsg
+473	common	mq_sendmmsg			sys_mq_sendmmsg		compat_sys_mq_sendmmsg
diff --git a/arch/s390/kernel/syscalls/syscall.tbl b/arch/s390/kernel/syscalls/syscall.tbl
index 09a7ef04d979..a3ce9c216477 100644
--- a/arch/s390/kernel/syscalls/syscall.tbl
+++ b/arch/s390/kernel/syscalls/syscall.tbl
@@ -398,3 +398,6 @@
 469	common	file_setattr			sys_file_setattr
 470	common	listns				sys_listns
 471	common	rseq_slice_yield		sys_rseq_slice_yield
+472	common	mq_recvmmsg			sys_mq_recvmmsg
+473	common	mq_sendmmsg			sys_mq_sendmmsg
+
diff --git a/arch/sh/kernel/syscalls/syscall.tbl b/arch/sh/kernel/syscalls/syscall.tbl
index 70b315cbe710..b14d8f4614c4 100644
--- a/arch/sh/kernel/syscalls/syscall.tbl
+++ b/arch/sh/kernel/syscalls/syscall.tbl
@@ -475,3 +475,5 @@
 469	common	file_setattr			sys_file_setattr
 470	common	listns				sys_listns
 471	common	rseq_slice_yield		sys_rseq_slice_yield
+472	common	mq_recvmmsg			sys_mq_recvmmsg
+473	common	mq_sendmmsg			sys_mq_sendmmsg
diff --git a/arch/sparc/kernel/syscalls/syscall.tbl b/arch/sparc/kernel/syscalls/syscall.tbl
index 7e71bf7fcd14..7ce825b9f531 100644
--- a/arch/sparc/kernel/syscalls/syscall.tbl
+++ b/arch/sparc/kernel/syscalls/syscall.tbl
@@ -517,3 +517,6 @@
 469	common	file_setattr			sys_file_setattr
 470	common	listns				sys_listns
 471	common	rseq_slice_yield		sys_rseq_slice_yield
+472	common	mq_recvmmsg			sys_mq_recvmmsg		compat_sys_mq_recvmmsg
+473	common	mq_sendmmsg			sys_mq_sendmmsg		compat_sys_mq_sendmmsg
+
diff --git a/arch/x86/entry/syscalls/syscall_32.tbl b/arch/x86/entry/syscalls/syscall_32.tbl
index f832ebd2d79b..b40cce591f20 100644
--- a/arch/x86/entry/syscalls/syscall_32.tbl
+++ b/arch/x86/entry/syscalls/syscall_32.tbl
@@ -477,3 +477,5 @@
 469	i386	file_setattr		sys_file_setattr
 470	i386	listns			sys_listns
 471	i386	rseq_slice_yield	sys_rseq_slice_yield
+472	i386	mq_recvmmsg		sys_mq_recvmmsg		compat_sys_mq_recvmmsg
+473	i386	mq_sendmmsg		sys_mq_sendmmsg		compat_sys_mq_sendmmsg
diff --git a/arch/x86/entry/syscalls/syscall_64.tbl b/arch/x86/entry/syscalls/syscall_64.tbl
index 524155d655da..16b29007e210 100644
--- a/arch/x86/entry/syscalls/syscall_64.tbl
+++ b/arch/x86/entry/syscalls/syscall_64.tbl
@@ -396,7 +396,8 @@
 469	common	file_setattr		sys_file_setattr
 470	common	listns			sys_listns
 471	common	rseq_slice_yield	sys_rseq_slice_yield
-
+472	common	mq_recvmmsg		sys_mq_recvmmsg
+473	common	mq_sendmmsg		sys_mq_sendmmsg
 #
 # Due to a historical design error, certain syscalls are numbered differently
 # in x32 as compared to native x86_64.  These syscalls have numbers 512-547.
diff --git a/arch/xtensa/kernel/syscalls/syscall.tbl b/arch/xtensa/kernel/syscalls/syscall.tbl
index a9bca4e484de..377f27d70a65 100644
--- a/arch/xtensa/kernel/syscalls/syscall.tbl
+++ b/arch/xtensa/kernel/syscalls/syscall.tbl
@@ -442,3 +442,5 @@
 469	common	file_setattr			sys_file_setattr
 470	common	listns				sys_listns
 471	common	rseq_slice_yield		sys_rseq_slice_yield
+472	common	mq_recvmmsg			sys_mq_recvmmsg
+473	common	mq_sendmmsg			sys_mq_sendmmsg
diff --git a/scripts/syscall.tbl b/scripts/syscall.tbl
index 7a42b32b6577..8e50ab85b951 100644
--- a/scripts/syscall.tbl
+++ b/scripts/syscall.tbl
@@ -412,3 +412,5 @@
 469	common	file_setattr			sys_file_setattr
 470	common	listns				sys_listns
 471	common	rseq_slice_yield		sys_rseq_slice_yield
+472	common	mq_recvmmsg			sys_mq_recvmmsg		compat_sys_mq_recvmmsg
+473	common	mq_sendmmsg			sys_mq_sendmmsg		compat_sys_mq_sendmmsg
-- 
2.43.0


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH v4 3/4] IPC: Added system call entry in performance tool
  2026-09-24 15:34 [PATCH v4 0/4] Add two new system call mq_recvmmsg() and mq_sendmmsg() to posix ipc mqueue mathura kumar
  2026-09-24 15:34 ` [PATCH v4 1/4] IPC: Added two new system call mq_recvmmsg() and mq_sendmmsg() mathura kumar
  2026-09-24 15:35 ` [PATCH v4 2/4] IPC: Added system call entry in all of most common architectures mathura kumar
@ 2026-09-24 15:35 ` mathura kumar
  2026-09-24 15:35 ` [PATCH v4 4/4] Test: Added self-testing and documentation mathura kumar
  3 siblings, 0 replies; 5+ messages in thread
From: mathura kumar @ 2026-09-24 15:35 UTC (permalink / raw)
  To: brauner; +Cc: linux-kernel, linux-arch, corbet, mathura.kumar.tech

It include all the entries  made up in performance tools
for both new system call.

Signed-off-by: mathura kumar <mathura.kumar.tech@gmail.com>
---
 tools/include/uapi/asm-generic/unistd.h             | 8 +++++++-
 tools/perf/arch/alpha/entry/syscalls/syscall.tbl    | 4 ++++
 tools/perf/arch/arm/entry/syscalls/syscall.tbl      | 3 +++
 tools/perf/arch/arm64/entry/syscalls/syscall_32.tbl | 3 +++
 tools/perf/arch/mips/entry/syscalls/syscall_n64.tbl | 2 ++
 tools/perf/arch/parisc/entry/syscalls/syscall.tbl   | 3 +++
 tools/perf/arch/powerpc/entry/syscalls/syscall.tbl  | 3 +++
 tools/perf/arch/s390/entry/syscalls/syscall.tbl     | 2 ++
 tools/perf/arch/sh/entry/syscalls/syscall.tbl       | 2 ++
 tools/perf/arch/sparc/entry/syscalls/syscall.tbl    | 2 ++
 tools/perf/arch/x86/entry/syscalls/syscall_32.tbl   | 2 ++
 tools/perf/arch/x86/entry/syscalls/syscall_64.tbl   | 2 ++
 tools/perf/arch/xtensa/entry/syscalls/syscall.tbl   | 2 ++
 tools/scripts/syscall.tbl                           | 2 ++
 14 files changed, 39 insertions(+), 1 deletion(-)

diff --git a/tools/include/uapi/asm-generic/unistd.h b/tools/include/uapi/asm-generic/unistd.h
index a627acc8fb5f..b891edfebdf9 100644
--- a/tools/include/uapi/asm-generic/unistd.h
+++ b/tools/include/uapi/asm-generic/unistd.h
@@ -863,8 +863,14 @@ __SYSCALL(__NR_listns, sys_listns)
 #define __NR_rseq_slice_yield 471
 __SYSCALL(__NR_rseq_slice_yield, sys_rseq_slice_yield)
 
+#define __NR_mq_recvmmsg 472
+__SC_COMP(__NR_mq_recvmmsg, sys_mq_recvmmsg, compat_sys_mq_recvmmsg)
+
+#define __NR_mq_sendmmsg 473
+__SC_COMP(__NR_mq_sendmmsg, sys_mq_sendmmsg, compat_sys_mq_sendmmsg)
+
 #undef __NR_syscalls
-#define __NR_syscalls 472
+#define __NR_syscalls 474
 
 /*
  * 32 bit systems traditionally used different
diff --git a/tools/perf/arch/alpha/entry/syscalls/syscall.tbl b/tools/perf/arch/alpha/entry/syscalls/syscall.tbl
index 74720667fe09..44cb7657bd44 100644
--- a/tools/perf/arch/alpha/entry/syscalls/syscall.tbl
+++ b/tools/perf/arch/alpha/entry/syscalls/syscall.tbl
@@ -502,3 +502,7 @@
 570	common	lsm_set_self_attr		sys_lsm_set_self_attr
 571	common	lsm_list_modules		sys_lsm_list_modules
 572	common  mseal				sys_mseal
+# 573-581 are kept reserved for existing system call
+582	common	mq_recvmmsg			sys_mq_recvmmsg
+583	common	mq_sendmmsg			sys_mq_sendmmsg
+
diff --git a/tools/perf/arch/arm/entry/syscalls/syscall.tbl b/tools/perf/arch/arm/entry/syscalls/syscall.tbl
index 94351e22bfcf..714b3964e31c 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..3d408a842d07 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..de1000dff765 100644
--- a/tools/perf/arch/mips/entry/syscalls/syscall_n64.tbl
+++ b/tools/perf/arch/mips/entry/syscalls/syscall_n64.tbl
@@ -386,3 +386,5 @@
 469	n64	file_setattr			sys_file_setattr
 470	n64	listns				sys_listns
 471	n64	rseq_slice_yield		sys_rseq_slice_yield
+472	n64	mq_recvmmsg			sys_mq_recvmmsg
+473	n64	mq_sendmmsg			sys_mq_sendmmsg
diff --git a/tools/perf/arch/parisc/entry/syscalls/syscall.tbl b/tools/perf/arch/parisc/entry/syscalls/syscall.tbl
index 66dc406b12e4..e1d442f17f25 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..4a9edd38da52 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..eba3ed5b7576 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..b14d8f4614c4 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..35ee3651e66c 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..b40cce591f20 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..c16fb5d613b2 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..377f27d70a65 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..8e50ab85b951 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	[flat|nested] 5+ messages in thread

* [PATCH v4 4/4] Test: Added self-testing and documentation
  2026-09-24 15:34 [PATCH v4 0/4] Add two new system call mq_recvmmsg() and mq_sendmmsg() to posix ipc mqueue mathura kumar
                   ` (2 preceding siblings ...)
  2026-09-24 15:35 ` [PATCH v4 3/4] IPC: Added system call entry in performance tool mathura kumar
@ 2026-09-24 15:35 ` mathura kumar
  3 siblings, 0 replies; 5+ messages in thread
From: mathura kumar @ 2026-09-24 15:35 UTC (permalink / raw)
  To: brauner; +Cc: linux-kernel, linux-arch, corbet, mathura.kumar.tech

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 <mathura.kumar.tech@gmail.com>
---
 Documentation/userspace-api/index.rst       |   2 +
 Documentation/userspace-api/mq_recvmmsg.rst | 207 +++++++++++
 Documentation/userspace-api/mq_sendmmsg.rst | 171 +++++++++
 tools/testing/selftests/ipc/Makefile        |   2 +-
 tools/testing/selftests/ipc/mq_recvmmsg.c   | 392 ++++++++++++++++++++
 tools/testing/selftests/ipc/mq_sendmmsg.c   | 360 ++++++++++++++++++
 6 files changed, 1133 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..680e5f1beb0c
--- /dev/null
+++ b/Documentation/userspace-api/mq_recvmmsg.rst
@@ -0,0 +1,207 @@
+.. 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 and passing a bad address in ret can
+          cause successfully dequeued messages to be permanently lost, as userspace has no other
+          way to know about messages already dequeued in the non-peek path.
+
+
+ERRORS
+        ``EAGAIN``
+                Queue is empty and MQ_PEEK is set. Peek is always
+                non-blocking and returns immediately on empty queue.
+
+        ``EBADF``
+                mqdes is not a valid message queue descriptor open
+                for reading.
+
+        ``EFAULT``
+                attrs, msg_ptr, msg_prio, or abs_timeout points to
+                an invalid address or any invalid address.
+
+        ``EINVAL``
+                The batch size is too large, start_idx is inconsistent with the
+                current queue state, flags contains an unrecognized value, or contradictory
+                flags were passed.
+
+        ``EMSGSIZE``
+                msg_len is less than the mq_msgsize attribute of
+                the queue.
+
+        ``E2BIG``
+                attrs_len is greater than PAGE_SIZE.
+
+        ``ETIMEDOUT``
+                Pop path only. The call timed out before a message
+                became available. Never returned on peek path.
+
+        ``ENODATA``
+                No valid message exists matching the given parameters.
+
+        ``ENOMEM``
+                 Insufficient memory to complete the operation.
+
+3) Design
+---------
+
+3.1 Struct-based argument passing
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The message buffer parameters (msg_ptr, msg_len, msg_prio) are consolidated
+into struct mq_msg_attrs rather than passed as individual syscall arguments.
+These are then referenced via struct mq_mmsg_attrs, which contains an
+iovec msg_attrs_vec and a vlen count. The base of msg_attrs_vec holds individual
+mq_msg_attrs entries, and length store struct mq_msg_attrs size.
+
+
+3.2 Compat handling
+~~~~~~~~~~~~~~~~~~~~
+
+.. code-block:: c
+
+        struct compat_msg_attrs {
+	        compat_size_t msg_len;
+	        compat_uptr_t msg_prio;
+	        compat_uptr_t msg_ptr;
+        };
+
+        struct compat_mq_mmsg_attrs {
+	        compat_uptr_t msg_attrs_vec;
+	        compat_size_t vlen;
+	        compat_uptr_t ret;
+        };
+
+
+The compat entry point performs the necessary conversions before
+calling the shared do_mq_recvmmsg() implementation.
+
+3.3 Peek implementation
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+When MQ_PEEK is set, the implementation locates the target message
+in the priority tree but does not remove it. Two locks are taken:
+the first confirms a message exists before any allocation is
+attempted and retrieve required buffer size to do memory allocation
+outside of critical section, avoiding allocation on empty queues.
+The second protects the kernel temporary buffer copy operation.
+
+3.4 start_idx argument
+~~~~~~~~~~~~~~~~~~~~~~~
+
+The start_idx argument specifies which message to begin operating on within the queue.
+If a batch request fails partway through, user space is expected to supply an updated
+start_idx reflecting the number of messages already handled, failing to do so may result
+in unexpected message delivery.
+
+4) Test Specification
+----------------------
+
+Tests for mq_recvmmsg() should cover the following:
+
+1) Basic receive and peek — Verify that without MQ_PEEK the message is consumed and
+the queue depth decreases by one. Verify that the message body, priority, and return
+value are correct.
+
+2) Offset and boundary handling — Verify that the implementation correctly respects
+batch size and start_idx boundary conditions.
+
+3) Partial success — Verify that when an error occurs mid-batch, the call returns
+successfully with the count of messages processed so far, rather than discarding partial
+results.
+
+
+
+
+
diff --git a/Documentation/userspace-api/mq_sendmmsg.rst b/Documentation/userspace-api/mq_sendmmsg.rst
new file mode 100644
index 000000000000..7b676645177e
--- /dev/null
+++ b/Documentation/userspace-api/mq_sendmmsg.rst
@@ -0,0 +1,171 @@
+.. 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 and passing a bad address in ret can
+          cause successfully enqueued messages to be processed again in a future call, resulting
+          in duplicates, as userspace has no other way to know about messages processed so far in
+          previous call.
+
+
+ERRORS
+        ``EAGAIN``
+                Queue is not empty.
+
+        ``EBADF``
+                mqdes is not a valid message queue descriptor open
+                for reading.
+
+        ``EFAULT``
+                attrs, msg_ptr, msg_prio, or abs_timeout points to
+                an invalid address.
+
+        ``EINVAL``
+                The batch size is too large, start_idx is inconsistent with the
+                current queue state.
+
+        ``EMSGSIZE``
+                msg_len is greater than the mq_msgsize attribute of
+                the queue.
+
+        ``E2BIG``
+                attrs_len is greater than PAGE_SIZE.
+
+        ``ETIMEDOUT``
+                The call timed out before a space became available to store
+                new messages.
+
+        ``ENOMEM``
+                 Insufficient memory to complete the operation.
+
+3) Design
+---------
+
+3.1 Struct-based argument passing
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The message buffer parameters (msg_ptr, msg_len, msg_prio) are consolidated
+into struct mq_msg_attrs rather than passed as individual syscall arguments.
+These are then referenced via struct mq_mmsg_attrs, which contains an
+iovec msg_attrs_vec and a vlen count. The base of msg_attrs_vec holds individual
+mq_msg_attrs entries, and length store struct mq_msg_attrs size.
+
+
+3.2 Compat handling
+~~~~~~~~~~~~~~~~~~~~
+
+.. code-block:: c
+
+        struct compat_msg_attrs {
+	        compat_size_t msg_len;
+	        compat_uptr_t msg_prio;
+	        compat_uptr_t msg_ptr;
+        };
+
+        struct compat_mq_mmsg_attrs {
+	        compat_uptr_t msg_attrs_vec;
+	        compat_size_t vlen;
+	        compat_uptr_t ret;
+        };
+
+
+The compat entry point performs the necessary conversions before
+calling the shared do_mq_sendmmsg() implementation.
+
+
+3.3 start_idx argument
+~~~~~~~~~~~~~~~~~~~~~~~
+
+The start_idx argument specifies the message at which to begin storing messages in 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 the 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..7d464c4d839f
--- /dev/null
+++ b/tools/testing/selftests/ipc/mq_recvmmsg.c
@@ -0,0 +1,392 @@
+// 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 <linux/time_types.h>
+#include "kselftest.h"
+#include <mqueue.h>
+
+#ifndef __NR_mq_recvmmsg
+#if defined(__alpha__)
+#define __NR_mq_recvmmsg 582
+#else
+#define __NR_mq_recvmmsg 472
+#endif
+#endif
+
+#define MQ_PEEK 0x02
+#define MQ_RECV 0x04
+
+#define MQ_NAME_PREFIX "/mq_peek_test"
+#define MQ_MAXMSG 16
+#define MAX_MSG_SIZE 8192
+
+struct msg_attrs {
+	long msg_len;
+	unsigned int *msg_prio;
+	void *msg_ptr;
+};
+
+struct mmsg_attrs {
+	struct iovec *msg_attrs_vec;
+	long vlen;
+	int *err;
+};
+
+struct batch_slot {
+	struct msg_attrs desc;
+	struct iovec iov;
+	char buf[MAX_MSG_SIZE];
+	unsigned int prio;
+};
+
+static long mq_recvmmsg(mqd_t mqdes, struct mmsg_attrs *attrs, unsigned int attrs_len,
+		       unsigned int flags, unsigned int start_idx,
+		       const struct __kernel_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 __kernel_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 __kernel_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\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..3f51ba75d1cb
--- /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 <linux/time_types.h>
+#include "kselftest.h"
+
+#ifndef __NR_mq_sendmmsg
+#if defined(__alpha__)
+#define __NR_mq_sendmmsg 583
+#else
+#define __NR_mq_sendmmsg 473
+#endif
+#endif
+
+#define MQ_NAME_PREFIX "/mq_sendmmsg_test_"
+#define MQ_MAXMSG 16
+#define MAX_MSG_SIZE 128
+
+struct msg_attrs {
+	long msg_len;
+	unsigned int *msg_prio;
+	void *msg_ptr;
+};
+
+struct mmsg_attrs {
+	struct iovec *msg_attrs_vec;
+	long vlen;
+	int *err;
+};
+
+struct send_slot {
+	struct msg_attrs desc;
+	struct iovec iov;
+	const char *payload;
+	unsigned int prio;
+};
+
+static long mq_sendmmsg(mqd_t mqdes, struct mmsg_attrs *attrs,
+		       unsigned int attrs_len, unsigned long start_idx,
+		       const struct __kernel_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, 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 __kernel_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];
+	static char *const 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];
+	static char *const 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];
+	static char *const 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];
+	static char *const 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 __kernel_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 __kernel_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];
+	static char *const 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	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2026-09-24 15:36 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-24 15:34 [PATCH v4 0/4] Add two new system call mq_recvmmsg() and mq_sendmmsg() to posix ipc mqueue mathura kumar
2026-09-24 15:34 ` [PATCH v4 1/4] IPC: Added two new system call mq_recvmmsg() and mq_sendmmsg() mathura kumar
2026-09-24 15:35 ` [PATCH v4 2/4] IPC: Added system call entry in all of most common architectures mathura kumar
2026-09-24 15:35 ` [PATCH v4 3/4] IPC: Added system call entry in performance tool mathura kumar
2026-09-24 15:35 ` [PATCH v4 4/4] Test: Added self-testing and documentation mathura kumar

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®