Hi tech@,

I implemented the sendmmsg and recvmmsg system calls by copying the
NetBSD implementation and adjusting it.

The idea behind the mmsg system calls is to do less systemcalls per
msghdr and thus improving throughput.
This should allow faster processing of packages (UDP, raw IP) in
userland.

int
sendmmsg(int s, const struct mmsghdr *mmsg, unsigned int vlen,
            unsigned int flags);

int
recvmmsg(int s, struct mmsghdr *mmsg, unsigned int vlen,
            unsigned int flags, struct timespec *timeout);

The interface ist incompatible to the other send and recv systemcalls
and the timeout feels unnecessary but this way it is compatible
with Linux and NetBSD.

The diff is below.
mbuhl

Index: lib/libc/Symbols.list
===================================================================
RCS file: /cvs/src/lib/libc/Symbols.list,v
retrieving revision 1.74
diff -u -p -r1.74 Symbols.list
--- lib/libc/Symbols.list       3 Jun 2021 13:19:45 -0000       1.74
+++ lib/libc/Symbols.list       22 Apr 2022 16:03:30 -0000
@@ -176,6 +176,7 @@ _thread_sys_readv
 _thread_sys_reboot
 _thread_sys_recvfrom
 _thread_sys_recvmsg
+_thread_sys_recvmmsg
 _thread_sys_rename
 _thread_sys_renameat
 _thread_sys_revoke
@@ -185,6 +186,7 @@ _thread_sys_select
 _thread_sys_semget
 _thread_sys_semop
 _thread_sys_sendmsg
+_thread_sys_sendmmsg
 _thread_sys_sendsyslog
 _thread_sys_sendto
 _thread_sys_setegid
@@ -373,6 +375,7 @@ readv
 reboot
 recvfrom
 recvmsg
+recvmmsg
 rename
 renameat
 revoke
@@ -384,6 +387,7 @@ semctl
 semget
 semop
 sendmsg
+sendmmsg
 sendsyslog
 sendto
 setegid
Index: lib/libc/shlib_version
===================================================================
RCS file: /cvs/src/lib/libc/shlib_version,v
retrieving revision 1.210
diff -u -p -r1.210 shlib_version
--- lib/libc/shlib_version      2 Jun 2021 07:29:03 -0000       1.210
+++ lib/libc/shlib_version      22 Apr 2022 16:03:30 -0000
@@ -1,4 +1,4 @@
 major=96
-minor=1
+minor=2
 # note: If changes were made to include/thread_private.h or if system calls
 # were added/changed then librthread/shlib_version must also be updated.
Index: lib/libc/hidden/sys/socket.h
===================================================================
RCS file: /cvs/src/lib/libc/hidden/sys/socket.h,v
retrieving revision 1.4
diff -u -p -r1.4 socket.h
--- lib/libc/hidden/sys/socket.h        7 May 2016 19:05:22 -0000       1.4
+++ lib/libc/hidden/sys/socket.h        22 Apr 2022 16:03:30 -0000
@@ -33,8 +33,10 @@ PROTO_NORMAL(listen);
 PROTO_NORMAL(recv);
 PROTO_CANCEL(recvfrom);
 PROTO_CANCEL(recvmsg);
+PROTO_CANCEL(recvmmsg);
 PROTO_NORMAL(send);
 PROTO_CANCEL(sendmsg);
+PROTO_CANCEL(sendmmsg);
 PROTO_CANCEL(sendto);
 PROTO_NORMAL(setrtable);
 PROTO_NORMAL(setsockopt);
Index: lib/libc/sys/Makefile.inc
===================================================================
RCS file: /cvs/src/lib/libc/sys/Makefile.inc,v
retrieving revision 1.161
diff -u -p -r1.161 Makefile.inc
--- lib/libc/sys/Makefile.inc   23 Dec 2021 18:50:32 -0000      1.161
+++ lib/libc/sys/Makefile.inc   22 Apr 2022 16:03:30 -0000
@@ -34,8 +34,8 @@ CANCEL=       accept accept4 \
        nanosleep \
        open openat \
        poll ppoll pread preadv pselect pwrite pwritev \
-       read readv recvfrom recvmsg \
-       select sendmsg sendto \
+       read readv recvfrom recvmsg recvmmsg \
+       select sendmsg sendmmsg sendto \
        wait4 write writev
 SRCS+= ${CANCEL:%=w_%.c}
 
Index: lib/libc/sys/recv.2
===================================================================
RCS file: /cvs/src/lib/libc/sys/recv.2,v
retrieving revision 1.48
diff -u -p -r1.48 recv.2
--- lib/libc/sys/recv.2 21 Nov 2021 23:44:55 -0000      1.48
+++ lib/libc/sys/recv.2 22 Apr 2022 16:03:30 -0000
@@ -46,15 +46,35 @@
 .Fn recvfrom "int s" "void *buf" "size_t len" "int flags" "struct sockaddr 
*from" "socklen_t *fromlen"
 .Ft ssize_t
 .Fn recvmsg "int s" "struct msghdr *msg" "int flags"
+.Ft int
+.Fn recvmmsg "int s" "struct mmsghdr *mmsg" "unsigned int vlen" "unsigned int 
flags" "struct timespec *timeout"
 .Sh DESCRIPTION
-.Fn recvfrom
+.Fn recv ,
+.Fn recvfrom ,
+.Fn recvmsg ,
 and
-.Fn recvmsg
+.Fn recvmmsg
 are used to receive messages from a socket,
-.Fa s ,
-and may be used to receive
+.Fa s .
+.Fn recv
+is normally used only on a
+.Em connected
+socket (see
+.Xr connect 2 ).
+.Fn recvfrom ,
+.Fn recvmsg ,
+and
+.Fn recvmmsg
+may be used to receive
 data on a socket whether or not it is connection-oriented.
 .Pp
+.Fn recv
+is identical to
+.Fn recvfrom
+with a null
+.Fa from
+parameter.
+.Pp
 If
 .Fa from
 is non-null and the socket is not connection-oriented,
@@ -66,25 +86,6 @@ the buffer associated with
 and modified on return to indicate the actual size of the
 address stored there.
 .Pp
-The
-.Fn recv
-call is normally used only on a
-.Em connected
-socket (see
-.Xr connect 2 )
-and is identical to
-.Fn recvfrom
-with a null
-.Fa from
-parameter.
-.Pp
-On successful completion, all three routines return the number of
-message bytes read.
-If a message is too long to fit in the supplied
-buffer, excess bytes may be discarded depending on the type of socket
-the message is received from (see
-.Xr socket 2 ) .
-.Pp
 If no messages are available at the socket, the
 receive call waits for a message to arrive, unless
 the socket is nonblocking (see
@@ -158,6 +159,8 @@ The
 .Dv MSG_CMSG_CLOEXEC
 requests that any file descriptors received as ancillary data with
 .Fn recvmsg
+and
+.Fn recvmmsg
 (see below)
 have their close-on-exec flag set.
 .Pp
@@ -249,13 +252,67 @@ Indicates that the packet was received a
 .It Dv MSG_MCAST
 Indicates that the packet was received as multicast.
 .El
+.Pp
+The
+.Fn recvmmsg
+call uses an array of the
+.Fa mmsghdr
+structure of length
+.Fa vlen
+to group multiple
+.Fa msghdr
+structures into a single system call.
+.Fa vlen
+is capped at maximum
+.Dv 1024
+messages that are received in a single call.
+The
+.Fa flags
+field allows setting
+.Dv MSG_WAITFORONE
+to wait for one
+.Fa msghdr ,
+and set
+.Dv MSG_DONTWAIT
+for all subsequent messages.
+A provided
+.Fa timeout
+limits the time spent in the function but it does not limit the
+time spent in lower parts of the kernel.
+.Pp
+The
+.Fa mmsghdr
+structure has the following form, as defined in
+.In sys/socket.h :
+.Bd -literal
+struct mmsghdr {
+       struct msghdr msg_hdr;
+       unsigned int msg_len;
+};
+.Ed
+.Pp
+Here
+.Fa msg_len
+indicated the number of bytes received for each
+.Fa msg_hdr
+member.
 .Sh RETURN VALUES
-These calls return the number of bytes received, or \-1 if an error occurred.
+The
+.Fn send ,
+.Fn sendto ,
+and
+.Fn sendmsg
+calls return the number of bytes received, or \-1 if an error occurred.
+The
+.Fn sendmmsg
+call returns the number of messages received, or \-1
+if an error occurred before the first message has been received. 
 .Sh ERRORS
 .Fn recv ,
 .Fn recvfrom ,
+.Fn recvmsg ,
 and
-.Fn recvmsg
+.Fn recvmmsg
 fail if:
 .Bl -tag -width "[EHOSTUNREACH]"
 .It Bq Er EBADF
@@ -310,6 +367,8 @@ was larger than
 .Pp
 And
 .Fn recvmsg
+and
+.Fn recvmmsg
 may return one of the following errors:
 .Bl -tag -width Er
 .It Bq Er EINVAL
@@ -364,6 +423,12 @@ The
 .Fn recv
 function call appeared in
 .Bx 4.1c .
+The
+.Fn sendmmsg
+syscall first appeared in Linux 2.6.33, was reimplemented for
+.Nx 7.0 ,
+and ported to
+.Ox 7.1 .
 .Sh CAVEATS
 Calling
 .Fn recvmsg
Index: lib/libc/sys/send.2
===================================================================
RCS file: /cvs/src/lib/libc/sys/send.2,v
retrieving revision 1.34
diff -u -p -r1.34 send.2
--- lib/libc/sys/send.2 11 Jan 2019 06:10:13 -0000      1.34
+++ lib/libc/sys/send.2 22 Apr 2022 16:03:30 -0000
@@ -36,7 +36,8 @@
 .Sh NAME
 .Nm send ,
 .Nm sendto ,
-.Nm sendmsg
+.Nm sendmsg ,
+.Nm sendmmsg
 .Nd send a message from a socket
 .Sh SYNOPSIS
 .In sys/socket.h
@@ -46,19 +47,23 @@
 .Fn sendto "int s" "const void *msg" "size_t len" "int flags" "const struct 
sockaddr *to" "socklen_t tolen"
 .Ft ssize_t
 .Fn sendmsg "int s" "const struct msghdr *msg" "int flags"
+.Ft int
+.Fn sendmmsg "int s" "const struct mmsghdr *mmsg" "unsigned int vlen" 
"unsigned int flags"
 .Sh DESCRIPTION
 .Fn send ,
 .Fn sendto ,
+.Fn sendmsg ,
 and
-.Fn sendmsg
+.Fn sendmmsg
 are used to transmit a message to another socket.
 .Fn send
 may be used only when the socket is in a
 .Em connected
 state, while
-.Fn sendto
+.Fn sendto ,
+.Fn sendmsg ,
 and
-.Fn sendmsg
+.Fn sendmmsg
 may be used at any time.
 .Pp
 The address of the target is given by
@@ -127,10 +132,21 @@ See
 .Xr recv 2
 for a description of the
 .Fa msghdr
-structure.
+and
+.Fa mmsghdr
+structures.
 .Sh RETURN VALUES
-The call returns the number of characters sent, or \-1
+The
+.Fn send ,
+.Fn sendto ,
+and
+.Fn sendmsg
+calls return the number of characters sent, or \-1
 if an error occurred.
+The
+.Fn sendmmsg
+call returns the number of messages sent, or \-1
+if an error occurred before the first message has been sent. 
 .Sh ERRORS
 .Fn send ,
 .Fn sendto ,
@@ -267,3 +283,9 @@ The
 .Fn send
 function call appeared in
 .Bx 4.1c .
+The
+.Fn sendmmsg
+syscall first appeared in Linux 3.0, was reimplemented for
+.Nx 7.0 ,
+and ported to
+.Ox 7.1 .
Index: lib/libc/sys/w_recvmmsg.c
===================================================================
RCS file: lib/libc/sys/w_recvmmsg.c
diff -N lib/libc/sys/w_recvmmsg.c
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ lib/libc/sys/w_recvmmsg.c   22 Apr 2022 16:03:30 -0000
@@ -0,0 +1,32 @@
+/*     $OpenBSD$ */
+/*
+ * Copyright (c) 2021 Moritz Buhl <[email protected]>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/socket.h>
+#include "cancel.h"
+
+int
+recvmmsg(int fd, struct mmsghdr *mmsg, unsigned int vlen, unsigned int flags,
+           struct timespec *ts)
+{
+       int ret;
+
+       ENTER_CANCEL_POINT(1);
+       ret = HIDDEN(recvmmsg)(fd, mmsg, vlen, flags, ts);
+       LEAVE_CANCEL_POINT(ret == -1);
+       return (ret);
+}
+DEF_CANCEL(recvmmsg);
Index: lib/libc/sys/w_sendmmsg.c
===================================================================
RCS file: lib/libc/sys/w_sendmmsg.c
diff -N lib/libc/sys/w_sendmmsg.c
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ lib/libc/sys/w_sendmmsg.c   22 Apr 2022 16:03:30 -0000
@@ -0,0 +1,31 @@
+/*     $OpenBSD$ */
+/*
+ * Copyright (c) 2021 Moritz Buhl <[email protected]>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/socket.h>
+#include "cancel.h"
+
+int
+sendmmsg(int s, struct mmsghdr *mmsg, unsigned int vlen, unsigned int flags)
+{
+       int ret;
+
+       ENTER_CANCEL_POINT(1);
+       ret = HIDDEN(sendmmsg)(s, mmsg, vlen, flags);
+       LEAVE_CANCEL_POINT(ret <= 0);
+       return (ret);
+}
+DEF_CANCEL(sendmmsg);
Index: regress/lib/libc/sys/Makefile
===================================================================
RCS file: /cvs/src/regress/lib/libc/sys/Makefile,v
retrieving revision 1.15
diff -u -p -r1.15 Makefile
--- regress/lib/libc/sys/Makefile       6 Jan 2022 03:30:15 -0000       1.15
+++ regress/lib/libc/sys/Makefile       22 Apr 2022 16:03:30 -0000
@@ -54,8 +54,10 @@ PROGS +=     t_pipe2
 PROGS +=       t_poll
 PROGS +=       t_ppoll
 PROGS +=       t_ptrace
+PROGS +=       t_recvmmsg
 PROGS +=       t_revoke
 PROGS +=       t_select
+PROGS +=       t_sendmmsg
 PROGS +=       t_sendrecv
 PROGS +=       t_setrlimit
 PROGS +=       t_setuid
Index: regress/lib/libc/sys/atf-c.h
===================================================================
RCS file: /cvs/src/regress/lib/libc/sys/atf-c.h,v
retrieving revision 1.3
diff -u -p -r1.3 atf-c.h
--- regress/lib/libc/sys/atf-c.h        2 Sep 2021 12:40:44 -0000       1.3
+++ regress/lib/libc/sys/atf-c.h        22 Apr 2022 16:03:30 -0000
@@ -76,6 +76,7 @@ ATF_TC_FUNCTIONS(fn)
 #define ATF_CHECK              ATF_REQUIRE
 #define ATF_CHECK_MSG          ATF_REQUIRE_MSG
 #define ATF_CHECK_EQ           ATF_REQUIRE_EQ
+#define ATF_CHECK_EQ_MSG       ATF_REQUIRE_EQ_MSG
 #define ATF_CHECK_ERRNO                ATF_REQUIRE_ERRNO
 #define ATF_CHECK_STREQ        ATF_REQUIRE_STREQ
 
Index: regress/lib/libc/sys/t_recvmmsg.c
===================================================================
RCS file: regress/lib/libc/sys/t_recvmmsg.c
diff -N regress/lib/libc/sys/t_recvmmsg.c
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ regress/lib/libc/sys/t_recvmmsg.c   22 Apr 2022 16:03:30 -0000
@@ -0,0 +1,188 @@
+/*     $NetBSD: t_recvmmsg.c,v 1.4 2018/08/21 10:39:21 christos Exp $  */
+
+/*-
+ * Copyright (c) 2012 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Jared McNeill and Christos Zoulas.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *        This product includes software developed by the NetBSD
+ *        Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <sys/cdefs.h>
+
+#include "atf-c.h"
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+
+#include <string.h>
+#include <time.h>
+#include <stdint.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sched.h>
+
+#define BUFSIZE        65536
+#define NPKTS  50
+
+#define min(a, b) ((a) < (b) ? (a) : (b))
+static int debug;
+static volatile sig_atomic_t rdied;
+
+static void
+handle_sigchld(__unused int pid)
+{
+
+       rdied = 1;
+}
+
+ATF_TC(recvmmsg_basic);
+ATF_TC_HEAD(recvmmsg_basic, tc)
+{
+       atf_tc_set_md_var(tc, "descr", "A basic test of recvmmsg(2)");
+}
+
+ATF_TC_BODY(recvmmsg_basic, tc)
+{
+       int fd[2], error, i, cnt;
+       uint8_t *buf;
+       struct mmsghdr *mmsghdr;
+       struct iovec *iov;
+       unsigned int mmsgcnt, n;
+       int status;
+       off_t off;
+       uint8_t DGRAM[1316] = { 0, 2, 3, 4, 5, 6, 7, 8, 9, };
+       struct sigaction sa;
+       ssize_t overf = 0;
+
+       error = socketpair(AF_UNIX, SOCK_DGRAM, 0, fd);
+       ATF_REQUIRE_MSG(error != -1, "socketpair failed (%s)", strerror(errno));
+
+       buf = malloc(BUFSIZE);
+       ATF_REQUIRE_MSG(buf != NULL, "malloc failed (%s)", strerror(errno));
+
+       mmsgcnt = BUFSIZE / sizeof(DGRAM);
+       mmsghdr = malloc(sizeof(*mmsghdr) * mmsgcnt);
+       ATF_REQUIRE_MSG(mmsghdr != NULL, "malloc failed (%s)", strerror(errno));
+       iov = malloc(sizeof(*iov) * mmsgcnt);
+       ATF_REQUIRE_MSG(iov != NULL, "malloc failed (%s)", strerror(errno));
+
+       for (off = 0, n = 0; n < mmsgcnt; n++) {
+               iov[n].iov_base = buf + off;
+               iov[n].iov_len = sizeof(DGRAM);
+               off += iov[n].iov_len;
+               mmsghdr[n].msg_hdr.msg_iov = &iov[n];
+               mmsghdr[n].msg_hdr.msg_iovlen = 1;
+               mmsghdr[n].msg_hdr.msg_name = NULL;
+               mmsghdr[n].msg_hdr.msg_namelen = 0;
+       }
+
+       memset(&sa, 0, sizeof(sa));
+       sa.sa_flags = SA_RESTART;
+       sa.sa_handler = &handle_sigchld;
+       sigemptyset(&sa.sa_mask);
+       error = sigaction(SIGCHLD, &sa, 0);
+       ATF_REQUIRE_MSG(error != -1, "sigaction failed (%s)",
+           strerror(errno));
+
+       switch (fork()) {
+       case -1:
+               ATF_REQUIRE_MSG(0, "fork failed (%s)", strerror(errno));
+               break;
+
+       case 0:
+               n = NPKTS;
+               if (debug)
+                   printf("waiting for %u messages (max %u per syscall)\n", n,
+                       mmsgcnt);
+               while (n > 0) {
+                       struct timespec ts = { 1, 0 };
+                       cnt = recvmmsg(fd[1], mmsghdr, min(mmsgcnt, n),
+                           MSG_WAITALL, &ts);
+                       if (cnt == -1 && errno == ENOBUFS) {
+                               overf++;
+                               if (debug)
+                                       printf("receive buffer overflowed"
+                                           " (%zu)\n",overf);
+                               continue;
+                       }
+                       ATF_REQUIRE_MSG(cnt != -1, "recvmmsg failed (%s)",
+                           strerror(errno));
+                       ATF_REQUIRE_MSG(cnt != 0, "recvmmsg timeout");
+                       if (debug)
+                               printf("recvmmsg: got %u messages\n", cnt);
+                       for (i = 0; i < cnt; i++) {
+                               ATF_CHECK_EQ_MSG(mmsghdr[i].msg_len,
+                                   sizeof(DGRAM), "packet length");
+                               ATF_CHECK_EQ_MSG(
+                                   ((uint8_t *)iov[i].iov_base)[0],
+                                   NPKTS - n + i, "packet contents");
+                       }
+                       n -= cnt;
+               }
+               if (debug)
+                       printf("done!\n");
+               exit(0);
+               /*NOTREACHED*/
+       default:
+               sched_yield();
+
+               for (n = 0; n < NPKTS; n++) {
+                       if (debug)
+                               printf("sending packet %u/%u...\n", (n+1),
+                                   NPKTS);
+                       do {
+                               if (rdied)
+                                       break;
+                               DGRAM[0] = n;
+                               error = send(fd[0], DGRAM, sizeof(DGRAM), 0);
+                       } while (error == -1 && errno == ENOBUFS);
+                       ATF_REQUIRE_MSG(error != -1, "send failed (%s)",
+                           strerror(errno));
+               }
+               error = wait(&status);
+               ATF_REQUIRE_MSG(error != -1, "wait failed (%s)",
+                   strerror(errno));
+               ATF_REQUIRE_MSG(WIFEXITED(status) && WEXITSTATUS(status) == 0,
+                   "receiver died");
+               break;
+       }
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+       ATF_TP_ADD_TC(tp, recvmmsg_basic);
+
+       return atf_no_error();
+}
Index: regress/lib/libc/sys/t_sendmmsg.c
===================================================================
RCS file: regress/lib/libc/sys/t_sendmmsg.c
diff -N regress/lib/libc/sys/t_sendmmsg.c
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ regress/lib/libc/sys/t_sendmmsg.c   22 Apr 2022 16:03:30 -0000
@@ -0,0 +1,211 @@
+/*     $NetBSD: t_sendmmsg.c,v 1.3 2019/03/16 21:46:43 christos Exp $  */
+
+/*-
+ * Copyright (c) 2018 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Christos Zoulas.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <sys/cdefs.h>
+
+#include "atf-c.h"
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/wait.h>
+
+#include <string.h>
+#include <time.h>
+#include <stdint.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sched.h>
+
+#define BUFSIZE        65536
+
+#define min(a, b) ((a) < (b) ? (a) : (b))
+static int debug = 1;
+static volatile sig_atomic_t rdied;
+
+static void
+handle_sigchld(__unused int pid)
+{
+
+       rdied = 1;
+}
+
+ATF_TC(sendmmsg_basic);
+ATF_TC_HEAD(sendmmsg_basic, tc)
+{
+       atf_tc_set_md_var(tc, "descr", "A basic test of sendmmsg(2)");
+}
+
+static void
+setsock(int fd, int type)
+{
+       int buflen = BUFSIZE;
+       socklen_t socklen = sizeof(buflen);
+
+       ATF_REQUIRE_MSG(setsockopt(fd, SOL_SOCKET, type,
+           &buflen, socklen) != -1, "%s (%s)",
+           type == SO_RCVBUF ? "rcv" : "snd", strerror(errno));
+}
+
+ATF_TC_BODY(sendmmsg_basic, tc)
+{
+       int fd[2], error, cnt;
+       uint8_t *buf;
+       struct mmsghdr *mmsghdr;
+       struct iovec *iov;
+       unsigned int mmsgcnt, n;
+       int status;
+       off_t off;
+       uint8_t DGRAM[1316] = { 0, 2, 3, 4, 5, 6, 7, 8, 9, };
+       uint8_t rgram[sizeof(DGRAM)];
+       struct sigaction sa;
+       ssize_t overf = 0;
+
+       error = socketpair(AF_UNIX, SOCK_DGRAM, 0, fd);
+       ATF_REQUIRE_MSG(error != -1, "socketpair failed (%s)", strerror(errno));
+
+       buf = malloc(BUFSIZE);
+       ATF_REQUIRE_MSG(buf != NULL, "malloc failed (%s)", strerror(errno));
+
+       setsock(fd[1], SO_SNDBUF);
+//     setsock(fd[0], SO_RCVBUF);
+
+       mmsgcnt = BUFSIZE / sizeof(DGRAM);
+       mmsghdr = calloc(mmsgcnt, sizeof(*mmsghdr));
+       ATF_REQUIRE_MSG(mmsghdr != NULL, "malloc failed (%s)", strerror(errno));
+       iov = malloc(sizeof(*iov) * mmsgcnt);
+       ATF_REQUIRE_MSG(iov != NULL, "malloc failed (%s)", strerror(errno));
+
+       for (off = 0, n = 0; n < mmsgcnt; n++) {
+               iov[n].iov_base = buf + off;
+               memcpy(iov[n].iov_base, DGRAM, sizeof(DGRAM));
+               *(buf + off) = n;
+               iov[n].iov_len = sizeof(DGRAM);
+               off += iov[n].iov_len;
+               mmsghdr[n].msg_hdr.msg_iov = &iov[n];
+               mmsghdr[n].msg_hdr.msg_iovlen = 1;
+               mmsghdr[n].msg_hdr.msg_name = NULL;
+               mmsghdr[n].msg_hdr.msg_namelen = 0;
+       }
+
+       memset(&sa, 0, sizeof(sa));
+       sa.sa_flags = SA_RESTART;
+       sa.sa_handler = &handle_sigchld;
+       sigemptyset(&sa.sa_mask);
+       error = sigaction(SIGCHLD, &sa, 0);
+       ATF_REQUIRE_MSG(error != -1, "sigaction failed (%s)",
+           strerror(errno));
+
+       switch (fork()) {
+       case -1:
+               ATF_REQUIRE_MSG(0, "fork failed (%s)", strerror(errno));
+               break;
+       case 0:
+               sched_yield();
+               if (debug)
+                   printf("sending %u messages (max %u per syscall)\n", n,
+                       mmsgcnt);
+               for (n = 0; n < mmsgcnt;) {
+                       if (debug)
+                               printf("sending packet %u/%u...\n", n,
+                                   mmsgcnt);
+                       // XXX: ENOBUFS bug, on the receive side!!!
+                       // in npkt = min(mmsgsize, mmsgcnt - n);
+                       int npkt = min(3, mmsgcnt - n), a;
+                       do {
+                               a = 0;
+                               ATF_REQUIRE(ioctl(fd[1], FIONSPACE, &a) != -1);
+                               printf("1 %d\n", a);
+                               ATF_REQUIRE(ioctl(fd[0], FIONSPACE, &a) != -1);
+                               printf("0 %d\n", a);
+                       } while ((size_t)a < sizeof(DGRAM));
+                       cnt = sendmmsg(fd[1], mmsghdr + n, npkt, 0);
+                       if (cnt == -1 && errno == ENOBUFS) {
+                               overf++;
+                               if (debug)
+                                       printf("send buffer overflowed"
+                                           " (%zu)\n",overf);
+                               if (overf > 100)
+                                       exit(1);
+                               sched_yield();
+                               sched_yield();
+                               sched_yield();
+                               continue;
+                       }
+                       ATF_REQUIRE_MSG(cnt != -1, "sendmmsg %u failed (%s)",
+                           n, strerror(errno));
+                       if (debug)
+                               printf("sendmmsg: sent %u messages\n", cnt);
+                       n += cnt;
+                       sched_yield();
+                       sched_yield();
+                       sched_yield();
+               }
+               if (debug)
+                       printf("done!\n");
+               exit(0);
+               /*NOTREACHED*/
+       default:
+               for (n = 0; n < mmsgcnt; n++) {
+                       if (debug)
+                               printf("receiving packet %u/%u...\n", n,
+                                   mmsgcnt);
+                       do {
+                               if (rdied)
+                                       break;
+                               cnt = recv(fd[0], rgram, sizeof(rgram), 0);
+                               ATF_REQUIRE_MSG(cnt != -1 || errno != ENOBUFS,
+                                   "recv failed (%s)", strerror(errno));
+                               ATF_CHECK_EQ_MSG(cnt, sizeof(rgram),
+                                   "packet length");
+                               ATF_CHECK_EQ_MSG(rgram[0], n,
+                                   "number %u != %u", rgram[0], n);
+                               ATF_REQUIRE_MSG(memcmp(rgram + 1, DGRAM + 1,
+                                   sizeof(rgram) - 1) == 0, "bad data");
+                       } while (cnt == -1 && errno == ENOBUFS);
+               }
+               error = wait(&status);
+               ATF_REQUIRE_MSG(error != -1, "wait failed (%s)",
+                   strerror(errno));
+               ATF_REQUIRE_MSG(WIFEXITED(status) && WEXITSTATUS(status) == 0,
+                   "receiver died");
+               break;
+       }
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+       ATF_TP_ADD_TC(tp, sendmmsg_basic);
+
+       return atf_no_error();
+}
Index: sys/kern/init_sysent.c
===================================================================
RCS file: /cvs/src/sys/kern/init_sysent.c,v
retrieving revision 1.235
diff -u -p -r1.235 init_sysent.c
--- sys/kern/init_sysent.c      24 Feb 2022 07:43:02 -0000      1.235
+++ sys/kern/init_sysent.c      22 Apr 2022 16:03:30 -0000
@@ -1,4 +1,4 @@
-/*     $OpenBSD: init_sysent.c,v 1.235 2022/02/24 07:43:02 mvs Exp $   */
+/*     $OpenBSD$       */
 
 /*
  * System call switch table.
@@ -751,5 +751,9 @@ const struct sysent sysent[] = {
            sys___set_tcb },                    /* 329 = __set_tcb */
        { 0, 0, SY_NOLOCK | 0,
            sys___get_tcb },                    /* 330 = __get_tcb */
+       { 5, s(struct sys_recvmmsg_args), SY_NOLOCK | 0,
+           sys_recvmmsg },                     /* 331 = recvmmsg */
+       { 4, s(struct sys_sendmmsg_args), SY_NOLOCK | 0,
+           sys_sendmmsg },                     /* 332 = sendmmsg */
 };
 
Index: sys/kern/syscalls.c
===================================================================
RCS file: /cvs/src/sys/kern/syscalls.c,v
retrieving revision 1.234
diff -u -p -r1.234 syscalls.c
--- sys/kern/syscalls.c 24 Feb 2022 07:43:03 -0000      1.234
+++ sys/kern/syscalls.c 22 Apr 2022 16:03:30 -0000
@@ -1,4 +1,4 @@
-/*     $OpenBSD: syscalls.c,v 1.234 2022/02/24 07:43:03 mvs Exp $      */
+/*     $OpenBSD$       */
 
 /*
  * System call names.
@@ -393,4 +393,6 @@ const char *const syscallnames[] = {
        "#328 (obsolete __tfork51)",            /* 328 = obsolete __tfork51 */
        "__set_tcb",                    /* 329 = __set_tcb */
        "__get_tcb",                    /* 330 = __get_tcb */
+       "recvmmsg",                     /* 331 = recvmmsg */
+       "sendmmsg",                     /* 332 = sendmmsg */
 };
Index: sys/kern/syscalls.master
===================================================================
RCS file: /cvs/src/sys/kern/syscalls.master,v
retrieving revision 1.223
diff -u -p -r1.223 syscalls.master
--- sys/kern/syscalls.master    24 Feb 2022 07:41:51 -0000      1.223
+++ sys/kern/syscalls.master    22 Apr 2022 16:03:30 -0000
@@ -575,3 +575,9 @@
 328    OBSOL           __tfork51
 329    STD NOLOCK      { void sys___set_tcb(void *tcb); }
 330    STD NOLOCK      { void *sys___get_tcb(void); }
+331    STD NOLOCK      { int sys_recvmmsg(int s, struct mmsghdr *mmsg, \
+                           unsigned int vlen, unsigned int flags, \
+                           struct timespec *timeout); }
+332    STD NOLOCK      { int sys_sendmmsg(int s,  \
+                           struct mmsghdr *mmsg, unsigned int vlen, \
+                           unsigned int flags); }
Index: sys/kern/uipc_syscalls.c
===================================================================
RCS file: /cvs/src/sys/kern/uipc_syscalls.c,v
retrieving revision 1.194
diff -u -p -r1.194 uipc_syscalls.c
--- sys/kern/uipc_syscalls.c    24 Oct 2021 00:02:25 -0000      1.194
+++ sys/kern/uipc_syscalls.c    22 Apr 2022 16:03:30 -0000
@@ -565,6 +565,84 @@ done:
 }
 
 int
+sys_sendmmsg(struct proc *p, void *v, register_t *retsize)
+{
+       struct sys_sendmmsg_args /* {
+               syscallarg(int)                 s;
+               syscallarg(struct mmsghdr *)    mmsg;
+               syscallarg(unsigned int)        vlen;
+               syscallarg(unsigned int)        flags;
+       } */ *uap = v;
+       struct mmsghdr mmsg;
+       struct iovec aiov[UIO_SMALLIOV], *iov = aiov, *uiov;
+       register_t retval;
+       unsigned int vlen, dg;
+       int error = 0;
+
+       vlen = SCARG(uap, vlen);
+       if (vlen > 1024)
+               vlen = 1024;
+
+       for (dg = 0; dg < vlen; dg++) {
+               error = copyin(SCARG(uap, mmsg) + dg, &mmsg, sizeof(mmsg));
+               if (error)
+                       break;
+
+#ifdef KTRACE
+               if (KTRPOINT(p, KTR_STRUCT))
+                       ktrmsghdr(p, &mmsg.msg_hdr);
+#endif
+
+               if (mmsg.msg_hdr.msg_iovlen > IOV_MAX)
+                       return (EMSGSIZE);
+               if (mmsg.msg_hdr.msg_iovlen > UIO_SMALLIOV)
+                       iov = mallocarray(mmsg.msg_hdr.msg_iovlen,
+                           sizeof(struct iovec), M_IOV, M_WAITOK);
+               else
+                       iov = aiov;
+
+               if (mmsg.msg_hdr.msg_iovlen != 0 &&
+                   (error = copyin(mmsg.msg_hdr.msg_iov, iov,
+                           mmsg.msg_hdr.msg_iovlen * sizeof (struct iovec))))
+                       break;
+
+#ifdef KTRACE
+               if (mmsg.msg_hdr.msg_iovlen && KTRPOINT(p, KTR_STRUCT))
+                       ktriovec(p, iov, mmsg.msg_hdr.msg_iovlen);
+#endif
+
+               uiov = mmsg.msg_hdr.msg_iov;
+               mmsg.msg_hdr.msg_iov = iov;
+               mmsg.msg_hdr.msg_flags = 0;
+               if ((error = sendit(p, SCARG(uap, s), &mmsg.msg_hdr, SCARG(uap, 
flags),
+                   &retval)) != 0)
+                       break;
+
+               mmsg.msg_hdr.msg_iov = uiov;
+               if (iov != aiov) {
+                       free(iov, M_IOV, sizeof(struct iovec) *
+                           mmsg.msg_hdr.msg_iovlen);
+                       iov = aiov;
+               }
+
+               mmsg.msg_len = retval;
+               error = copyout(&mmsg, SCARG(uap, mmsg) + dg, sizeof(mmsg));
+               if (error)
+                       break;
+       }
+
+       if (iov != aiov)
+               free(iov, M_IOV, sizeof(struct iovec) *
+                   mmsg.msg_hdr.msg_iovlen);
+
+       *retsize = dg;
+
+       if (dg)
+               return 0;
+       return error;
+}
+
+int
 sendit(struct proc *p, int s, struct msghdr *mp, int flags, register_t 
*retsize)
 {
        struct file *fp;
@@ -761,6 +839,126 @@ done:
        if (iov != aiov)
                free(iov, M_IOV, sizeof(struct iovec) * msg.msg_iovlen);
        return (error);
+}
+
+int
+sys_recvmmsg(struct proc *p, void *v, register_t *retval)
+{
+       struct sys_recvmmsg_args /* {
+               syscallarg(int)                 s;
+               syscallarg(struct mmsghdr *)    mmsg;
+               syscallarg(unsigned int)        vlen;
+               syscallarg(unsigned int)        flags;
+               syscallarg(struct timespec *)   timeout;
+       } */ *uap = v;
+       struct mmsghdr mmsg;
+       struct timespec ts, now;
+       struct iovec aiov[UIO_SMALLIOV], *uiov, *iov = aiov;
+       struct timespec *timeout;
+       unsigned int vlen, dg;
+       int error = 0, flags;
+
+       timeout = SCARG(uap, timeout);
+       if (timeout != NULL) {
+               error = copyin(SCARG(uap, timeout), &ts, sizeof(ts));
+               if (error != 0)
+                       return error;
+               getnanotime(&now);
+               timespecadd(&now, &ts, &ts);
+       }
+
+       flags = SCARG(uap, flags);
+
+       vlen = SCARG(uap, vlen);
+       if (vlen > 1024)
+               vlen = 1024;
+
+       for (dg = 0; dg < vlen;) {
+               error = copyin(SCARG(uap, mmsg) + dg, &mmsg, sizeof(mmsg));
+               if (error != 0)
+                       break;
+
+               if (mmsg.msg_hdr.msg_iovlen > IOV_MAX) {
+                       error = EMSGSIZE;
+                       break;
+               }
+
+               if (mmsg.msg_hdr.msg_iovlen > UIO_SMALLIOV)
+                       iov = mallocarray(mmsg.msg_hdr.msg_iovlen,
+                           sizeof(struct iovec), M_IOV, M_WAITOK);
+               else
+                       iov = aiov;
+
+               if (mmsg.msg_hdr.msg_iovlen > 0) {
+                       error = copyin(mmsg.msg_hdr.msg_iov, iov,
+                           mmsg.msg_hdr.msg_iovlen * sizeof(struct iovec));
+                       if (error)
+                               break;
+               }
+
+               uiov = mmsg.msg_hdr.msg_iov;
+               mmsg.msg_hdr.msg_iov = iov;
+               mmsg.msg_hdr.msg_flags = flags;
+
+               error = recvit(p, SCARG(uap, s), &mmsg.msg_hdr, NULL, retval);
+               if (error != 0) {
+                       if (error == EAGAIN && dg > 0)
+                               error = 0;
+                       break;
+               }
+
+               if (dg == 0 && flags & MSG_WAITFORONE) {
+                       flags &= ~MSG_WAITFORONE;
+                       flags |= MSG_DONTWAIT;
+               }
+
+               mmsg.msg_hdr.msg_iov = uiov;
+               mmsg.msg_len = *retval;
+#ifdef KTRACE
+               if (KTRPOINT(p, KTR_STRUCT)) {
+                       ktrmsghdr(p, &mmsg.msg_hdr);
+                       if (mmsg.msg_hdr.msg_iovlen)
+                               ktriovec(p, iov, mmsg.msg_hdr.msg_iovlen);
+               }
+#endif
+
+               error = copyout(&mmsg, SCARG(uap, mmsg) + dg, sizeof(mmsg));
+               if (error != 0)
+                       break;
+
+               if (iov != aiov) {
+                       free(iov, M_IOV, sizeof(struct iovec) *
+                           mmsg.msg_hdr.msg_iovlen);
+                       iov = aiov;
+               }
+
+               dg++;
+               if (mmsg.msg_hdr.msg_flags & MSG_OOB)
+                       break;
+
+               if (timeout != NULL) {
+                       getnanotime(&now);
+                       timespecsub(&now, &ts, &now);
+                       if (now.tv_sec > 0)
+                               break;
+               }
+       }
+
+       *retval = dg;
+
+       /*
+        * If we succeeded at least once, return 0, hopefully so->so_rerror
+        * will catch it next time.
+        */
+       if (error && dg > 0) {
+               //so->so_rerror = error; // XXX
+               error = 0;
+       }
+
+       if (iov != aiov)
+               free(iov, M_IOV, sizeof(struct iovec) * 
mmsg.msg_hdr.msg_iovlen);
+
+       return error;
 }
 
 int
Index: sys/sys/socket.h
===================================================================
RCS file: /cvs/src/sys/sys/socket.h,v
retrieving revision 1.102
diff -u -p -r1.102 socket.h
--- sys/sys/socket.h    22 Feb 2022 01:01:02 -0000      1.102
+++ sys/sys/socket.h    22 Apr 2022 16:03:30 -0000
@@ -490,6 +490,11 @@ struct msghdr {
        int             msg_flags;      /* flags on received message */
 };
 
+struct mmsghdr {
+       struct msghdr msg_hdr;
+       unsigned int msg_len;
+};
+
 #define        MSG_OOB                 0x1     /* process out-of-band data */
 #define        MSG_PEEK                0x2     /* peek at incoming message */
 #define        MSG_DONTROUTE           0x4     /* send without using routing 
tables */
@@ -502,6 +507,7 @@ struct msghdr {
 #define        MSG_MCAST               0x200   /* this message rec'd as 
multicast */
 #define        MSG_NOSIGNAL            0x400   /* do not send SIGPIPE */
 #define        MSG_CMSG_CLOEXEC        0x800   /* set FD_CLOEXEC on received 
fds */
+#define        MSG_WAITFORONE          0x1000  /* nonblocking but wait for one 
msg */
 
 /*
  * Header for ancillary data objects in msg_control buffer.
@@ -574,6 +580,10 @@ int        shutdown(int, int);
 int    sockatmark(int);
 int    socket(int, int, int);
 int    socketpair(int, int, int, int *);
+int    sendmmsg(int, struct mmsghdr *, unsigned int, unsigned int);
+struct timespec;
+int    recvmmsg(int, struct mmsghdr *, unsigned int, unsigned int,
+           struct timespec *);
 
 #if __BSD_VISIBLE
 int    accept4(int, struct sockaddr *__restrict, socklen_t *__restrict, int);
Index: sys/sys/syscall.h
===================================================================
RCS file: /cvs/src/sys/sys/syscall.h,v
retrieving revision 1.232
diff -u -p -r1.232 syscall.h
--- sys/sys/syscall.h   24 Feb 2022 07:43:03 -0000      1.232
+++ sys/sys/syscall.h   22 Apr 2022 16:03:30 -0000
@@ -1,4 +1,4 @@
-/*     $OpenBSD: syscall.h,v 1.232 2022/02/24 07:43:03 mvs Exp $       */
+/*     $OpenBSD$       */
 
 /*
  * System call numbers.
@@ -728,4 +728,10 @@
 /* syscall: "__get_tcb" ret: "void *" args: */
 #define        SYS___get_tcb   330
 
-#define        SYS_MAXSYSCALL  331
+/* syscall: "recvmmsg" ret: "int" args: "int" "struct mmsghdr *" "unsigned 
int" "unsigned int" "struct timespec *" */
+#define        SYS_recvmmsg    331
+
+/* syscall: "sendmmsg" ret: "int" args: "int" "struct mmsghdr *" "unsigned 
int" "unsigned int" */
+#define        SYS_sendmmsg    332
+
+#define        SYS_MAXSYSCALL  333
Index: sys/sys/syscallargs.h
===================================================================
RCS file: /cvs/src/sys/sys/syscallargs.h,v
retrieving revision 1.235
diff -u -p -r1.235 syscallargs.h
--- sys/sys/syscallargs.h       24 Feb 2022 07:43:03 -0000      1.235
+++ sys/sys/syscallargs.h       22 Apr 2022 16:03:30 -0000
@@ -1,4 +1,4 @@
-/*     $OpenBSD: syscallargs.h,v 1.235 2022/02/24 07:43:03 mvs Exp $   */
+/*     $OpenBSD$       */
 
 /*
  * System call argument lists.
@@ -1171,6 +1171,21 @@ struct sys___set_tcb_args {
        syscallarg(void *) tcb;
 };
 
+struct sys_recvmmsg_args {
+       syscallarg(int) s;
+       syscallarg(struct mmsghdr *) mmsg;
+       syscallarg(unsigned int) vlen;
+       syscallarg(unsigned int) flags;
+       syscallarg(struct timespec *) timeout;
+};
+
+struct sys_sendmmsg_args {
+       syscallarg(int) s;
+       syscallarg(struct mmsghdr *) mmsg;
+       syscallarg(unsigned int) vlen;
+       syscallarg(unsigned int) flags;
+};
+
 /*
  * System call prototypes.
  */
@@ -1431,3 +1446,5 @@ int       sys_symlinkat(struct proc *, void *,
 int    sys_unlinkat(struct proc *, void *, register_t *);
 int    sys___set_tcb(struct proc *, void *, register_t *);
 int    sys___get_tcb(struct proc *, void *, register_t *);
+int    sys_recvmmsg(struct proc *, void *, register_t *);
+int    sys_sendmmsg(struct proc *, void *, register_t *);

Reply via email to