Re: [PATCH v3 1/5]IPC: Added New system call do_mq_timedreceive2() for non-destructive peek on posix mqueue

Andrei Vagin <[email protected]> Mon, 13 Apr 2026 09:34:36 -0700
Newsgroups dev.linux.lists.criu
Message-ID <CANaxB-ytiyRbuiEj51H_UjOg6+D7jrFNYPacXpViDCOrWu9gSA@mail.gmail.com>
Hi Mathura_Kumar,

First of all, please read
https://www.kernel.org/doc/html/v4.10/process/submitting-patches.html.

You need to write a detailed commit message for each patch.

Before sending a patch, it is recommended to run checkpatch:
$ ./scripts/checkpatch.pl `git format-patch HEAD~1`
WARNING: Missing commit description - Add an appropriate one

WARNING: line length of 107 exceeds 100 columns
#38: FILE: include/linux/compat.h:805:
+asmlinkage long compat_sys_mq_timedreceive2(mqd_t mqdes, struct
compat_mq_timedreceive2_args __user *uargs,

WARNING: line length of 128 exceeds 100 columns
#39: FILE: include/linux/compat.h:806:
+ unsigned int flags, unsigned long index,

WARNING: line length of 131 exceeds 100 columns
#40: FILE: include/linux/compat.h:807:
+ struct old_timespec32 __user *abs_timeout);

WARNING: Use #include <linux/compat.h> instead of <asm/compat.h>
#104: FILE: include/uapi/linux/mqueue.h:22:
+#include <asm/compat.h>

WARNING: line length of 122 exceeds 100 columns
#306: FILE: ipc/mqueue.c:1636:
+ struct compat_mq_timedreceive2_args __user *uargs)

total: 0 errors, 6 warnings, 408 lines checked


On Wed, Apr 8, 2026 at 2:53=E2=80=AFAM Mathura_Kumar <academic1mathura@gmai=
l.com> wrote:
>
> Signed-off-by: Mathura_Kumar <[email protected]>
> ---
>  include/linux/compat.h            |   6 +-
>  include/linux/syscalls.h          |   6 +
>  include/uapi/asm-generic/unistd.h |   7 +-
>  include/uapi/linux/mqueue.h       |  14 ++-
>  ipc/mqueue.c                      | 186 ++++++++++++++++++++++++++++--
>  ipc/msg.c                         |   2 +-
>  ipc/msgutil.c                     |  48 ++++----
>  ipc/util.h                        |   3 +-
>  kernel/sys_ni.c                   |   1 +
>  9 files changed, 231 insertions(+), 42 deletions(-)
>
> diff --git a/include/linux/compat.h b/include/linux/compat.h
> index 56cebaff0c91..9f5ca26e76d8 100644
> --- a/include/linux/compat.h
> +++ b/include/linux/compat.h
> @@ -22,6 +22,7 @@
>  #include <asm/compat.h>
>  #include <asm/siginfo.h>
>  #include <asm/signal.h>
> +#include <linux/mqueue.h>
>
>  #ifdef CONFIG_ARCH_HAS_SYSCALL_WRAPPER
>  /*
> @@ -801,8 +802,9 @@ 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_timedreceive2(mqd_t mqdes, struct compat_m=
q_timedreceive2_args __user *uargs,
> +                                                                        =
               unsigned int flags, unsigned long index,
> +                                                                        =
               struct old_timespec32 __user *abs_timeout);
>  /*
>   * Deprecated system calls which are still defined in
>   * include/uapi/asm-generic/unistd.h and wanted by >=3D 1 arch
> diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h
> index 02bd6ddb6278..993e570c90ab 100644
> --- a/include/linux/syscalls.h
> +++ b/include/linux/syscalls.h
> @@ -79,6 +79,7 @@ struct mnt_id_req;
>  struct ns_id_req;
>  struct xattr_args;
>  struct file_attr;
> +struct mq_timedreceive2_args;
>
>  #include <linux/types.h>
>  #include <linux/aio_abi.h>
> @@ -93,6 +94,7 @@ struct file_attr;
>  #include <linux/key.h>
>  #include <linux/personality.h>
>  #include <trace/syscall.h>
> +#include <linux/mqueue.h>
>
>  #ifdef CONFIG_ARCH_HAS_SYSCALL_WRAPPER
>  /*
> @@ -746,6 +748,10 @@ asmlinkage long sys_mq_timedsend_time32(mqd_t mqdes,
>                         const char __user *u_msg_ptr,
>                         unsigned int msg_len, unsigned int msg_prio,
>                         const struct old_timespec32 __user *u_abs_timeout=
);
> +asmlinkage long
> +sys_mq_timedreceive2(mqd_t mqdes, struct mq_timedreceive2_args __user *u=
args,
> +                    unsigned int flags, unsigned long index,
> +                    struct __kernel_timespec __user *abs_timeout);
>  asmlinkage long sys_msgget(key_t key, int msgflg);
>  asmlinkage long sys_old_msgctl(int msqid, int cmd, struct msqid_ds __use=
r *buf);
>  asmlinkage long sys_msgctl(int msqid, int cmd, struct msqid_ds __user *b=
uf);
> diff --git a/include/uapi/asm-generic/unistd.h b/include/uapi/asm-generic=
/unistd.h
> index a627acc8fb5f..200ee7fde5c4 100644
> --- a/include/uapi/asm-generic/unistd.h
> +++ b/include/uapi/asm-generic/unistd.h
> @@ -863,9 +863,12 @@ __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_timedreceive2 472
> +__SC_COMP(__NR_mq_timedreceive2, sys_mq_timedreceive2,
> +               compat_sys_mq_timedreceive2)
>
> +#undef __NR_syscalls
> +#define __NR_syscalls 473
>  /*
>   * 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..7cdced63f5d2 100644
> --- a/include/uapi/linux/mqueue.h
> +++ b/include/uapi/linux/mqueue.h
> @@ -18,8 +18,8 @@
>
>  #ifndef _LINUX_MQUEUE_H
>  #define _LINUX_MQUEUE_H
> -
>  #include <linux/types.h>
> +#include <asm/compat.h>
>
>  #define MQ_PRIO_MAX    32768
>  /* per-uid limit of kernel memory used by mqueue, in bytes */
> @@ -33,6 +33,18 @@ struct mq_attr {
>         __kernel_long_t __reserved[4];  /* ignored for input, zeroed for =
output */
>  };
>
> +struct mq_timedreceive2_args {
> +       size_t msg_len;
> +       unsigned int  *msg_prio;
> +       char  *msg_ptr;
> +};
> +
> +struct compat_mq_timedreceive2_args {
> +       compat_size_t msg_len;
> +       compat_uptr_t msg_prio;
> +       compat_uptr_t msg_ptr;
> +};
> +
>  /*
>   * SIGEV_THREAD implementation:
>   * SIGEV_THREAD must be implemented in user space. If SIGEV_THREAD is pa=
ssed
> diff --git a/ipc/mqueue.c b/ipc/mqueue.c
> index 4798b375972b..78dc414967a2 100644
> --- a/ipc/mqueue.c
> +++ b/ipc/mqueue.c
> @@ -53,6 +53,7 @@ struct mqueue_fs_context {
>
>  #define SEND           0
>  #define RECV           1
> +#define MQ_PEEK     2
>
>  #define STATE_NONE     0
>  #define STATE_READY    1
> @@ -1230,6 +1231,115 @@ static int do_mq_timedreceive(mqd_t mqdes, char _=
_user *u_msg_ptr,
>         return ret;
>  }
>
> +static struct msg_msg *mq_peek_index(struct mqueue_inode_info *info, int=
 index)
> +{
> +       struct rb_node *node;
> +       struct posix_msg_tree_node *leaf;
> +       struct msg_msg *msg;
> +
> +       int count =3D 0;
> +
> +       /* Start from highest priority */
> +       node =3D rb_last(&info->msg_tree);
> +       while (node) {
> +               leaf =3D rb_entry(node, struct posix_msg_tree_node, rb_no=
de);
> +               list_for_each_entry(msg, &leaf->msg_list, m_list) {
> +                       if (count =3D=3D index)
> +                               return msg;
> +                       count++;
> +               }
> +
> +               node =3D rb_prev(node);
> +       }
> +
> +       return NULL;
> +}
> +
> +static int do_mq_timedreceive2(mqd_t mqdes, struct mq_timedreceive2_args=
 *args,
> +                              unsigned int flags, unsigned long index,
> +                              struct timespec64 *ts)
> +{
> +       ssize_t ret;
> +       struct msg_msg *msg_ptr, *k_msg_buffer;
> +       long k_m_type;
> +       size_t k_m_ts;
> +       struct inode *inode;
> +       struct mqueue_inode_info *info;
> +
> +       if (!(flags & MQ_PEEK)) {
> +               return do_mq_timedreceive(mqdes, args->msg_ptr, args->msg=
_len,
> +                                         args->msg_prio, ts);
> +       }
> +       audit_mq_sendrecv(mqdes, args->msg_len, 0, ts);
> +       CLASS(fd, f)(mqdes);
> +       if (fd_empty(f))
> +               return -EBADF;
> +
> +       inode =3D file_inode(fd_file(f));
> +       if (unlikely(fd_file(f)->f_op !=3D &mqueue_file_operations))
> +               return -EBADF;
> +       info =3D MQUEUE_I(inode);
> +       audit_file(fd_file(f));
> +
> +       if (unlikely(!(fd_file(f)->f_mode & FMODE_READ)))
> +               return -EBADF;
> +
> +       if (unlikely(args->msg_len < info->attr.mq_msgsize))
> +               return -EMSGSIZE;
> +       if (index >=3D (unsigned long)info->attr.mq_maxmsg)
> +               return -ENOENT;
> +
> +       spin_lock(&info->lock);
> +       if (info->attr.mq_curmsgs =3D=3D 0) {
> +               spin_unlock(&info->lock);
> +               return -EAGAIN;

should it be ENOENT?

> +       }
> +       msg_ptr =3D mq_peek_index(info, index);
> +       if (!msg_ptr) {
> +               spin_unlock(&info->lock);
> +               return -ENOENT;
> +       }
> +       k_m_type =3D msg_ptr->m_type;
> +       k_m_ts =3D msg_ptr->m_ts;
> +       spin_unlock(&info->lock);
> +
> +       k_msg_buffer =3D alloc_msg(k_m_ts);
> +       if (!k_msg_buffer)
> +               return -ENOMEM;
> +
> +       /*
> +        * Two spin locks are necessary here. We are avoiding atomic memo=
ry
> +        * allocation and premature allocation before confirming that
> +        * a message actually exists to peek.
> +        */

MSG_COPY doesn't require to lock the spinlock twice, so why can't we
do the same thing here?

> +       spin_lock(&info->lock);
> +       msg_ptr =3D mq_peek_index(info, index);
> +       if (!msg_ptr || msg_ptr->m_type !=3D k_m_type ||
> +           msg_ptr->m_ts !=3D k_m_ts) {
> +               spin_unlock(&info->lock);
> +               free_msg(k_msg_buffer);
> +               return -EAGAIN;
> +       }
> +       if (IS_ERR(copy_msg(msg_ptr, k_msg_buffer, k_m_ts))) {
> +               spin_unlock(&info->lock);
> +               free_msg(k_msg_buffer);
> +               return -EINVAL;

you probably need to return the code returned by copy_msg...

> +       }
> +       spin_unlock(&info->lock);
> +
> +       ret =3D k_msg_buffer->m_ts;
> +       if (args->msg_prio && put_user(k_m_type, args->msg_prio)) {
> +               free_msg(k_msg_buffer);
> +               return -EFAULT;
> +       }
> +       if (store_msg(args->msg_ptr, k_msg_buffer, k_m_ts)) {
> +               free_msg(k_msg_buffer);
> +               return -EFAULT;
> +       }
> +       free_msg(k_msg_buffer);
> +       return ret;
> +}
> +
>  SYSCALL_DEFINE5(mq_timedsend, mqd_t, mqdes, const char __user *, u_msg_p=
tr,
>                 size_t, msg_len, unsigned int, msg_prio,
>                 const struct __kernel_timespec __user *, u_abs_timeout)
> @@ -1258,6 +1368,27 @@ SYSCALL_DEFINE5(mq_timedreceive, mqd_t, mqdes, cha=
r __user *, u_msg_ptr,
>         return do_mq_timedreceive(mqdes, u_msg_ptr, msg_len, u_msg_prio, =
p);
>  }
>
> +SYSCALL_DEFINE5(mq_timedreceive2, mqd_t, mqdes,
> +               struct mq_timedreceive2_args __user *, uargs, unsigned in=
t,
> +               flags, const unsigned long, index,
> +               const struct __kernel_timespec __user *, u_abs_timeout)
> +{
> +       struct mq_timedreceive2_args args;
> +       struct timespec64 ts, *p =3D NULL;
> +
> +       if (copy_from_user(&args, uargs, sizeof(args)))
> +               return -EFAULT;
> +
> +       if (u_abs_timeout) {
> +               int res =3D prepare_timeout(u_abs_timeout, &ts);
> +
> +               if (res)
> +                       return res;
> +               p =3D &ts;
> +       }
> +       return do_mq_timedreceive2(mqdes, &args, flags, index, p);
> +}
> +
>  /*
>   * Notes: the case when user wants us to deregister (with NULL as pointe=
r)
>   * and he isn't currently owner of notification, will be silently discar=
ded.
> @@ -1449,6 +1580,17 @@ SYSCALL_DEFINE3(mq_getsetattr, mqd_t, mqdes,
>         return 0;
>  }
>
> +#ifdef CONFIG_COMPAT_32BIT_TIME
> +static int compat_prepare_timeout(const struct old_timespec32 __user *p,
> +                                                               struct ti=
mespec64 *ts)
> +{
> +       if (get_old_timespec32(ts, p))
> +               return -EFAULT;
> +       if (!timespec64_valid(ts))
> +               return -EINVAL;
> +       return 0;
> +}
> +
>  #ifdef CONFIG_COMPAT
>
>  struct compat_mq_attr {
> @@ -1490,6 +1632,22 @@ static inline int put_compat_mq_attr(const struct =
mq_attr *attr,
>         return 0;
>  }
>
> +static inline int get_compat_mq_args(struct mq_timedreceive2_args *args,
> +                                                                       s=
truct compat_mq_timedreceive2_args __user *uargs)
> +{
> +       struct compat_mq_timedreceive2_args v;
> +
> +       if (copy_from_user(&v, uargs, sizeof(v)))
> +               return -EFAULT;
> +
> +       memset(args, 0, sizeof(*args));
> +       args->msg_len =3D (size_t)v.msg_len;
> +       args->msg_prio =3D (unsigned int *)compat_ptr(v.msg_prio);
> +       args->msg_ptr =3D (char *)compat_ptr(v.msg_ptr);
> +
> +       return 0;
> +}
> +
>  COMPAT_SYSCALL_DEFINE4(mq_open, const char __user *, u_name,
>                        int, oflag, compat_mode_t, mode,
>                        struct compat_mq_attr __user *, u_attr)
> @@ -1541,19 +1699,30 @@ COMPAT_SYSCALL_DEFINE3(mq_getsetattr, mqd_t, mqde=
s,
>                 return -EFAULT;
>         return 0;
>  }
> -#endif
>
> -#ifdef CONFIG_COMPAT_32BIT_TIME
> -static int compat_prepare_timeout(const struct old_timespec32 __user *p,
> -                                  struct timespec64 *ts)
> +COMPAT_SYSCALL_DEFINE5(mq_timedreceive2, mqd_t, mqdes,
> +                      struct compat_mq_timedreceive2_args __user *, uarg=
s,
> +                      unsigned int, flags, const unsigned long, index,
> +                      const struct old_timespec32 __user *, u_abs_timeou=
t)
>  {
> -       if (get_old_timespec32(ts, p))
> +       struct mq_timedreceive2_args args;
> +       struct timespec64 ts, *p =3D NULL;
> +
> +       if (get_compat_mq_args(&args, uargs))
>                 return -EFAULT;
> -       if (!timespec64_valid(ts))
> -               return -EINVAL;
> -       return 0;
> +
> +       if (u_abs_timeout) {
> +               int res =3D compat_prepare_timeout(u_abs_timeout, &ts);
> +
> +               if (res)
> +                       return res;
> +               p =3D &ts;
> +       }
> +       return do_mq_timedreceive2(mqdes, &args, flags, index, p);
>  }
>
> +#endif
> +
>  SYSCALL_DEFINE5(mq_timedsend_time32, mqd_t, mqdes,
>                 const char __user *, u_msg_ptr,
>                 unsigned int, msg_len, unsigned int, msg_prio,
> @@ -1583,6 +1752,7 @@ SYSCALL_DEFINE5(mq_timedreceive_time32, mqd_t, mqde=
s,
>         }
>         return do_mq_timedreceive(mqdes, u_msg_ptr, msg_len, u_msg_prio, =
p);
>  }
> +
>  #endif
>
>  static const struct inode_operations mqueue_dir_inode_operations =3D {
> diff --git a/ipc/msg.c b/ipc/msg.c
> index 62996b97f0ac..6392b11dd7f7 100644
> --- a/ipc/msg.c
> +++ b/ipc/msg.c
> @@ -1156,7 +1156,7 @@ static long do_msgrcv(int msqid, void __user *buf, =
size_t bufsz, long msgtyp, in
>                          * not update queue parameters.
>                          */
>                         if (msgflg & MSG_COPY) {
> -                               msg =3D copy_msg(msg, copy);
> +                               msg =3D copy_msg(msg, copy, msg->m_ts);
>                                 goto out_unlock0;
>                         }
>
> diff --git a/ipc/msgutil.c b/ipc/msgutil.c
> index e28f0cecb2ec..c5536ce47cc2 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,33 @@ struct msg_msg *load_msg(const void __user *src, si=
ze_t len)
>         free_msg(msg);
>         return ERR_PTR(err);
>  }
> -#ifdef CONFIG_CHECKPOINT_RESTORE
> -struct msg_msg *copy_msg(struct msg_msg *src, struct msg_msg *dst)
> +
> +struct msg_msg *copy_msg(struct msg_msg *src, struct msg_msg *dst, size_=
t len)
>  {
> -       struct msg_msgseg *dst_pseg, *src_pseg;
> -       size_t len =3D src->m_ts;
> -       size_t alen;
> +       struct msg_msgseg *src_seg, *dst_seg;
> +       size_t remaining, chunk;
>
> -       if (src->m_ts > dst->m_ts)
> +       if (len > src->m_ts)
>                 return ERR_PTR(-EINVAL);
> -
> -       alen =3D min(len, DATALEN_MSG);
> -       memcpy(dst + 1, src + 1, alen);
> -
> -       for (dst_pseg =3D dst->next, src_pseg =3D src->next;
> -            src_pseg !=3D NULL;
> -            dst_pseg =3D dst_pseg->next, src_pseg =3D src_pseg->next) {
> -
> -               len -=3D alen;
> -               alen =3D min(len, DATALEN_SEG);
> -               memcpy(dst_pseg + 1, src_pseg + 1, alen);
> +       chunk =3D min(len, DATALEN_MSG);
> +       memcpy(dst + 1, src + 1, chunk);
> +       remaining =3D len - chunk;
> +       src_seg =3D src->next;
> +       dst_seg =3D dst->next;
> +       while (remaining > 0 && src_seg && dst_seg) {
> +               chunk =3D min(remaining, DATALEN_SEG);
> +               memcpy(dst_seg + 1, src_seg + 1, chunk);
> +               remaining -=3D chunk;
> +               src_seg =3D src_seg->next;
> +               dst_seg =3D dst_seg->next;
>         }
> -
> +       if (remaining !=3D 0)
> +               return ERR_PTR(-EINVAL);
>         dst->m_type =3D src->m_type;
> -       dst->m_ts =3D src->m_ts;
> -
> +       dst->m_ts   =3D src->m_ts;
>         return dst;
>  }
> -#else
> -struct msg_msg *copy_msg(struct msg_msg *src, struct msg_msg *dst)
> -{
> -       return ERR_PTR(-ENOSYS);
> -}
> -#endif
> +
>  int store_msg(void __user *dest, struct msg_msg *msg, size_t len)
>  {
>         size_t alen;
> diff --git a/ipc/util.h b/ipc/util.h
> index a55d6cebe6d3..374abeee79b3 100644
> --- a/ipc/util.h
> +++ b/ipc/util.h
> @@ -197,8 +197,9 @@ int ipc_parse_version(int *cmd);
>
>  extern void free_msg(struct msg_msg *msg);
>  extern struct msg_msg *load_msg(const void __user *src, size_t len);
> -extern struct msg_msg *copy_msg(struct msg_msg *src, struct msg_msg *dst=
);
> +extern struct msg_msg *copy_msg(struct msg_msg *src, struct msg_msg *dst=
, size_t len);
>  extern int store_msg(void __user *dest, struct msg_msg *msg, size_t len)=
;
> +extern struct msg_msg *alloc_msg(size_t len);
>
>  static inline int ipc_checkid(struct kern_ipc_perm *ipcp, int id)
>  {
> diff --git a/kernel/sys_ni.c b/kernel/sys_ni.c
> index add3032da16f..658d6b8274b3 100644
> --- a/kernel/sys_ni.c
> +++ b/kernel/sys_ni.c
> @@ -391,6 +391,7 @@ COND_SYSCALL(setuid16);
>  /* restartable sequence */
>  COND_SYSCALL(rseq);
>  COND_SYSCALL(rseq_slice_yield);
> +COND_SYSCALL(mq_timedreceive2);
>
>  COND_SYSCALL(uretprobe);
>  COND_SYSCALL(uprobe);
> --
> 2.43.0
>