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