Does FreeBSD have sendmmsg or recvmmsg system calls?

Boris Astardzhiev boris.astardzhiev at gmail.com
Mon Jan 25 09:22:16 UTC 2016


kb>You defer to ppoll() and pselect() due to the struct timespec type of
kb>the argument, am I right ?
Yes.

kb>BTW, just noted, I think the functions should live in libc/gen.
Okay. Moving them.

kb>Put each sentence on new line
Done.

kb>The CMTR define is only used once.  I do not see why not inline it, and
kb>get rid of the staircase of backslashes.
Initially I wrote two CMTRs - one using pselect and the other using ppoll.
I left it this way to easily switch between these and that's why I have
this macro. I'll inline the code as suggested.

kb>Also, it seems that the next unused bit is 0x80000.
I noticed that as well and fixed it.

kb>Still int for msg_len ?
I'll convert it to ssize_t.

kb>It probably makes sense to mark pointers with __restrict.
Done.

gj>This seems wrong.  rcvd is initialized to 1 so that the check for
gj>rcvd != 0 can never be false.  We already successfully called
gj>__sys_recvmsg() just above the loop, so why not simplify the
gj>code by doing
gj>
gj>        if (ret == -1)
gj>                return (rcvd);

Fixed.

On Sun, Jan 24, 2016 at 11:07 AM, Gary Jennejohn <gljennjohn at gmail.com>
wrote:

> On Sun, 24 Jan 2016 07:06:34 +0200
> Konstantin Belousov <kostikbel at gmail.com> wrote:
>
> [delete irrelevant parts of the patch]
>
> > > +   rcvd = 1;
> > > +   for (i = rcvd; i < vlen; i++) {
> > i = rcvd = 1; ... i++, rcvd++ ?
> >
> > > +           ret = __sys_recvmsg(s, &msgvec[i].msg_hdr, flags);
> > > +           if (ret == -1) {
> > > +                   if (rcvd != 0) {
> > > +                           /* We've received messages. Let caller
> know. */
>
> > > +                           return (rcvd);
> > > +                   }
> > > +                   return (ret);
> > > +           }
> > > +
>
> This seems wrong.  rcvd is initialized to 1 so that the check for
> rcvd != 0 can never be false.  We already successfully called
> __sys_recvmsg() just above the loop, so why not simplify the
> code by doing
>
>         if (ret == -1)
>                 return (rcvd);
>
> --
> Gary Jennejohn (gj@)
>
-------------- next part --------------
diff --git a/lib/libc/gen/Makefile.inc b/lib/libc/gen/Makefile.inc
index b448461..f9d2f8a 100644
--- a/lib/libc/gen/Makefile.inc
+++ b/lib/libc/gen/Makefile.inc
@@ -99,11 +99,13 @@ SRCS+=	__getosreldate.c \
 	raise.c \
 	readdir.c \
 	readpassphrase.c \
+	recvmmsg.c \
 	rewinddir.c \
 	scandir.c \
 	seed48.c \
 	seekdir.c \
 	semctl.c \
+	sendmmsg.c \
 	setdomainname.c \
 	sethostname.c \
 	setjmperr.c \
diff --git a/lib/libc/gen/recvmmsg.c b/lib/libc/gen/recvmmsg.c
new file mode 100644
index 0000000..98835ba
--- /dev/null
+++ b/lib/libc/gen/recvmmsg.c
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2016 Boris Astardzhiev, Smartcom-Bulgaria AD
+ * All rights reserved.
+ *
+ * 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(s), this list of conditions and the following disclaimer as
+ *    the first lines of this file unmodified other than the possible
+ *    addition of one or more copyright notices.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice(s), 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 COPYRIGHT HOLDER(S) ``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 COPYRIGHT HOLDER(S) 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>
+__FBSDID("$FreeBSD$");
+
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/syscall.h>
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <pthread.h>
+#include "libc_private.h"
+
+ssize_t
+recvmmsg(int s, struct mmsghdr *__restrict msgvec, size_t vlen, int flags,
+    const struct timespec *__restrict timeout)
+{
+	size_t i, rcvd;
+	ssize_t ret;
+
+	if (timeout != NULL) {
+		fd_set fds;
+		int res;
+
+		FD_ZERO(&fds);
+		FD_SET(s, &fds);
+		res = __sys_pselect(s + 1, &fds, NULL, NULL, timeout, NULL);
+		if (res == -1 || res == 0)
+			return (res);
+		if (!FD_ISSET(s, &fds))
+			return (-1);
+	}
+
+	ret = __sys_recvmsg(s, &msgvec[0].msg_hdr, flags);
+	if (ret == -1)
+		return (ret);
+
+	/* Check initially for the presence of MSG_WAITFORONE.
+	 * Turn on MSG_DONTWAIT if set. */
+	if (flags & MSG_WAITFORONE) {
+		flags |= MSG_DONTWAIT;
+		/* The kernel doesn't need to know about this flag. */
+		flags &= ~MSG_WAITFORONE;
+	}
+
+	rcvd = 1;
+	for (i = rcvd; i < vlen; i++, rcvd++) {
+		ret = __sys_recvmsg(s, &msgvec[i].msg_hdr, flags);
+		if (ret == -1) {
+			/* We have received messages. Let caller know. */
+			return (rcvd);
+		}
+
+		/* Save received bytes */
+		msgvec[i].msg_len = ret;
+	}
+
+	return (rcvd);
+}
diff --git a/lib/libc/gen/sendmmsg.c b/lib/libc/gen/sendmmsg.c
new file mode 100644
index 0000000..e4e62a4
--- /dev/null
+++ b/lib/libc/gen/sendmmsg.c
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2016 Boris Astardzhiev, Smartcom-Bulgaria AD
+ * All rights reserved.
+ *
+ * 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(s), this list of conditions and the following disclaimer as
+ *    the first lines of this file unmodified other than the possible
+ *    addition of one or more copyright notices.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice(s), 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 COPYRIGHT HOLDER(S) ``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 COPYRIGHT HOLDER(S) 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>
+__FBSDID("$FreeBSD$");
+
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/syscall.h>
+#include <sys/socket.h>
+#include <pthread.h>
+#include "libc_private.h"
+
+ssize_t
+sendmmsg(int s, struct mmsghdr *__restrict msgvec, size_t vlen, int flags)
+{
+	size_t i, sent;
+	ssize_t ret;
+
+	sent = 0;
+	for (i = 0; i < vlen; i++, sent++) {
+		ret = __sys_sendmsg(s, &msgvec[i].msg_hdr, flags);
+		if (ret == -1) {
+			if (sent != 0) {
+				/* We have sent messages. Let caller know. */
+				return (sent);
+			}
+			return (ret);
+		}
+
+		/* Save sent bytes */
+		msgvec[i].msg_len = ret;
+	}
+
+	return (sent);
+}
diff --git a/lib/libc/include/namespace.h b/lib/libc/include/namespace.h
index 739d7b1..c95829e 100644
--- a/lib/libc/include/namespace.h
+++ b/lib/libc/include/namespace.h
@@ -208,6 +208,7 @@
 #define		readv				_readv
 #define		recvfrom			_recvfrom
 #define		recvmsg				_recvmsg
+#define		recvmmsg			_recvmmsg
 #define		select				_select
 #define		sem_close			_sem_close
 #define		sem_destroy			_sem_destroy
@@ -220,6 +221,7 @@
 #define		sem_unlink			_sem_unlink
 #define		sem_wait			_sem_wait
 #define		sendmsg				_sendmsg
+#define		sendmmsg			_sendmmsg
 #define		sendto				_sendto
 #define		setsockopt			_setsockopt
 /*#define		sigaction			_sigaction*/
diff --git a/lib/libc/include/un-namespace.h b/lib/libc/include/un-namespace.h
index f31fa7a..0233348 100644
--- a/lib/libc/include/un-namespace.h
+++ b/lib/libc/include/un-namespace.h
@@ -189,6 +189,7 @@
 #undef		readv
 #undef		recvfrom
 #undef		recvmsg
+#undef		recvmmsg
 #undef		select
 #undef		sem_close
 #undef		sem_destroy
@@ -201,6 +202,7 @@
 #undef		sem_unlink
 #undef		sem_wait
 #undef		sendmsg
+#undef		sendmmsg
 #undef		sendto
 #undef		setsockopt
 #undef		sigaction
diff --git a/lib/libc/sys/Symbol.map b/lib/libc/sys/Symbol.map
index 7b3257c..dc2ed0e 100644
--- a/lib/libc/sys/Symbol.map
+++ b/lib/libc/sys/Symbol.map
@@ -399,6 +399,8 @@ FBSD_1.4 {
 	utimensat;
 	numa_setaffinity;
 	numa_getaffinity;
+	sendmmsg;
+	recvmmsg;
 };
 
 FBSDprivate_1.0 {
diff --git a/lib/libc/sys/recv.2 b/lib/libc/sys/recv.2
index 326e7ff..e940364 100644
--- a/lib/libc/sys/recv.2
+++ b/lib/libc/sys/recv.2
@@ -34,8 +34,9 @@
 .Sh NAME
 .Nm recv ,
 .Nm recvfrom ,
-.Nm recvmsg
-.Nd receive a message from a socket
+.Nm recvmsg ,
+.Nm recvmmsg
+.Nd receive message(s) from a socket
 .Sh LIBRARY
 .Lb libc
 .Sh SYNOPSIS
@@ -47,11 +48,15 @@
 .Fn recvfrom "int s" "void *buf" "size_t len" "int flags" "struct sockaddr * restrict from" "socklen_t * restrict fromlen"
 .Ft ssize_t
 .Fn recvmsg "int s" "struct msghdr *msg" "int flags"
+.Ft ssize_t
+.Fn recvmmsg "int s" "struct mmsghdr * restrict msgvec" "size_t vlen" "int flags" "const struct timespec * restrict timeout"
 .Sh DESCRIPTION
 The
 .Fn recvfrom
 and
 .Fn recvmsg
+and
+.Fn recvmmsg
 system calls
 are used to receive messages from a socket,
 and may be used to receive data on a socket whether or not
@@ -84,8 +89,29 @@ null pointer passed as its
 .Fa from
 argument.
 .Pp
-All three routines return the length of the message on successful
-completion.
+The
+.Fn recvmmsg
+function is used to receive multiple
+messages at a call.
+Their number is supplied by
+.Fa vlen .
+The messages are placed in the
+.Fa msgvec
+vector after reception.
+The size of each received message is placed in the
+.Fa msg_len
+field of each element of the vector.
+If
+.Fa timeout
+is NULL the call will normally block.
+Otherwise it will wait for data for the specified amount of time.
+If the timeout expires and there is no data received a value of 0 is returned.
+pselect(2) is used for the implementation of the timeout mechanism.
+.Pp
+The first three routines return the length of the message on successful
+completion whereas
+.Fn recvmmsg
+returns the number of received messages.
 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
@@ -100,7 +126,9 @@ in which case the value
 .Va errno
 is set to
 .Er EAGAIN .
-The receive calls normally return any data available,
+The receive calls except
+.Fn recvmmsg
+normally return any data available,
 up to the requested amount,
 rather than waiting for receipt of the full amount requested;
 this behavior is affected by the socket-level options
@@ -127,6 +155,9 @@ one or more of the values:
 .It Dv MSG_WAITALL Ta wait for full request or error
 .It Dv MSG_DONTWAIT Ta do not block
 .It Dv MSG_CMSG_CLOEXEC Ta set received fds close-on-exec
+.It Dv MSG_WAITFORONE Ta do not block after receiving the first message
+(relevant only for
+.Fn recvmmsg )
 .El
 .Pp
 The
@@ -158,6 +189,11 @@ is set to
 This flag is not available in strict
 .Tn ANSI
 or C99 compilation mode.
+The
+.Dv MSG_WAITFORONE
+flag sets MSG_DONTWAIT after the first message has been received.
+This flag is only relevant for
+.Fn recvmmsg .
 .Pp
 The
 .Fn recvmsg
@@ -290,9 +326,33 @@ control data were discarded due to lack of space in the buffer
 for ancillary data.
 .Dv MSG_OOB
 is returned to indicate that expedited or out-of-band data were received.
+.Pp
+The
+.Fn recvmmsg
+system call uses the
+.Fa mmsghdr
+structure. Its form is as follows, as defined in
+.In sys/socket.h :
+.Bd -literal
+struct mmsghdr {
+	struct msghdr	 msg_hdr;	/* message header */
+	ssize_t		 msg_len;	/* message length */
+};
+.Ed
+.Pp
+For
+.Fa msg_hdr
+see above. On data reception the
+.Fa msg_len
+field is updated to the length of the received message.
+On data transmission it is updated to the number of characters sent.
 .Sh RETURN VALUES
-These calls return the number of bytes received, or -1
-if an error occurred.
+These calls except
+.Fn recvmmsg
+return the number of bytes received.
+.Fn recvmmsg
+returns the number of messages received.
+A value of -1 is returned if an error occurred.
 .Sh ERRORS
 The calls fail if:
 .Bl -tag -width Er
diff --git a/lib/libc/sys/send.2 b/lib/libc/sys/send.2
index 8fa2c64..9810ace 100644
--- a/lib/libc/sys/send.2
+++ b/lib/libc/sys/send.2
@@ -34,8 +34,9 @@
 .Sh NAME
 .Nm send ,
 .Nm sendto ,
-.Nm sendmsg
-.Nd send a message from a socket
+.Nm sendmsg ,
+.Nm sendmmsg
+.Nd send message(s) from a socket
 .Sh LIBRARY
 .Lb libc
 .Sh SYNOPSIS
@@ -47,6 +48,8 @@
 .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 ssize_t
+.Fn sendmmsg "int s" "struct mmsghdr * restrict msgvec" "size_t vlen" "int flags"
 .Sh DESCRIPTION
 The
 .Fn send
@@ -55,8 +58,11 @@ and
 .Fn sendto
 and
 .Fn sendmsg
+and
+.Fn sendmmsg
 system calls
-are used to transmit a message to another socket.
+are used to transmit one or multiple messages (with the latter call) to
+another socket.
 The
 .Fn send
 function
@@ -66,6 +72,8 @@ state, while
 .Fn sendto
 and
 .Fn sendmsg
+and
+.Fn sendmmsg
 may be used at any time.
 .Pp
 The address of the target is given by
@@ -81,6 +89,18 @@ underlying protocol, the error
 is returned, and
 the message is not transmitted.
 .Pp
+The
+.Fn sendmmsg
+function sends multiple messages at a call.
+They are given by the
+.Fa msgvec
+vector along with
+.Fa vlen
+specifying its size.
+The number of characters sent per each message is placed in the
+.Fa msg_len
+field of each element of the vector after transmission.
+.Pp
 No indication of failure to deliver is implicit in a
 .Fn send .
 Locally detected errors are indicated by a return value of -1.
@@ -138,10 +158,16 @@ See
 .Xr recv 2
 for a description of the
 .Fa msghdr
+structure and the
+.Fa mmsghdr
 structure.
 .Sh RETURN VALUES
-The call returns the number of characters sent, or -1
-if an error occurred.
+All calls except
+.Fn sendmmsg
+return the number of characters sent. The
+.Fn sendmmsg
+call returns the number of messages sent.
+If an error occurred a value of -1 is returned.
 .Sh ERRORS
 The
 .Fn send
@@ -149,6 +175,8 @@ function and
 .Fn sendto
 and
 .Fn sendmsg
+and
+.Fn sendmmsg
 system calls
 fail if:
 .Bl -tag -width Er
diff --git a/sys/sys/socket.h b/sys/sys/socket.h
index 18e2de1..c7a21cc 100644
--- a/sys/sys/socket.h
+++ b/sys/sys/socket.h
@@ -431,6 +431,9 @@ struct msghdr {
 #define	MSG_NBIO	0x4000		/* FIONBIO mode, used by fifofs */
 #define	MSG_COMPAT      0x8000		/* used in sendit() */
 #define	MSG_CMSG_CLOEXEC 0x40000	/* make received fds close-on-exec */
+#ifndef _KERNEL
+#define MSG_WAITFORONE	0x80000		/* for recvmmsg() */
+#endif /* !_KERNEL */
 #endif
 #ifdef _KERNEL
 #define	MSG_SOCALLBCK   0x10000		/* for use by socket callbacks - soreceive (TCP) */
@@ -595,6 +598,18 @@ struct sf_hdtr {
 #endif /* _KERNEL */
 #endif /* __BSD_VISIBLE */
 
+#ifndef _KERNEL
+#ifdef __BSD_VISIBLE
+/*
+ * Send/recvmmsg specific structure(s)
+ */
+struct mmsghdr {
+	struct msghdr	msg_hdr;		/* message header */
+	ssize_t		msg_len;		/* message length */
+};
+#endif /* __BSD_VISIBLE */
+#endif /* !_KERNEL */
+
 #ifndef	_KERNEL
 
 #include <sys/cdefs.h>
@@ -615,11 +630,19 @@ int	listen(int, int);
 ssize_t	recv(int, void *, size_t, int);
 ssize_t	recvfrom(int, void *, size_t, int, struct sockaddr * __restrict, socklen_t * __restrict);
 ssize_t	recvmsg(int, struct msghdr *, int);
+#if __BSD_VISIBLE
+struct timespec;
+ssize_t	recvmmsg(int, struct mmsghdr * __restrict, size_t, int,
+    const struct timespec * __restrict);
+#endif
 ssize_t	send(int, const void *, size_t, int);
 ssize_t	sendto(int, const void *,
 	    size_t, int, const struct sockaddr *, socklen_t);
 ssize_t	sendmsg(int, const struct msghdr *, int);
 #if __BSD_VISIBLE
+ssize_t	sendmmsg(int, struct mmsghdr * __restrict, size_t, int);
+#endif
+#if __BSD_VISIBLE
 int	sendfile(int, int, off_t, size_t, struct sf_hdtr *, off_t *, int);
 int	setfib(int);
 #endif


More information about the freebsd-threads mailing list