Skip to content

Commit

Permalink
socket-util: Introduce emulation and wrapper for recvmmsg().
Browse files Browse the repository at this point in the history
Not every system will have recvmmsg(), so introduce compatibility code
that will allow it to be used blindly from the rest of the tree.

This assumes that recvmmsg() and sendmmsg() are either both present or
both absent in system libraries and headers.

CC: Yi Yang <[email protected]>
Signed-off-by: Ben Pfaff <[email protected]>
  • Loading branch information
blp committed Jan 9, 2020
1 parent b926f57 commit b901898
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 11 deletions.
5 changes: 4 additions & 1 deletion include/sparse/sys/socket.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@

typedef unsigned short int sa_family_t;
typedef __socklen_t socklen_t;
struct timespec;

struct sockaddr {
sa_family_t sa_family;
Expand Down Expand Up @@ -126,7 +127,8 @@ enum {
MSG_PEEK,
MSG_TRUNC,
MSG_WAITALL,
MSG_DONTWAIT
MSG_DONTWAIT,
MSG_WAITFORONE
};

enum {
Expand Down Expand Up @@ -163,6 +165,7 @@ ssize_t recvmsg(int, struct msghdr *, int);
ssize_t send(int, const void *, size_t, int);
ssize_t sendmsg(int, const struct msghdr *, int);
int sendmmsg(int, struct mmsghdr *, unsigned int, unsigned int);
int recvmmsg(int, struct mmsghdr *, unsigned int, int, struct timespec *);
ssize_t sendto(int, const void *, size_t, int, const struct sockaddr *,
socklen_t);
int setsockopt(int, int, int, const void *, socklen_t);
Expand Down
56 changes: 56 additions & 0 deletions lib/socket-util.c
Original file line number Diff line number Diff line change
Expand Up @@ -1283,3 +1283,59 @@ wrap_sendmmsg(int fd, struct mmsghdr *msgs, unsigned int n, unsigned int flags)
}
#endif
#endif

#ifndef _WIN32 /* Avoid using recvmsg on Windows entirely. */
static int
emulate_recvmmsg(int fd, struct mmsghdr *msgs, unsigned int n,
int flags, struct timespec *timeout OVS_UNUSED)
{
ovs_assert(!timeout); /* XXX not emulated */

bool waitforone = flags & MSG_WAITFORONE;
flags &= ~MSG_WAITFORONE;

for (unsigned int i = 0; i < n; i++) {
ssize_t retval = recvmsg(fd, &msgs[i].msg_hdr, flags);
if (retval < 0) {
return i ? i : retval;
}
msgs[i].msg_len = retval;

if (waitforone) {
flags |= MSG_DONTWAIT;
}
}
return n;
}

#ifndef HAVE_SENDMMSG
int
recvmmsg(int fd, struct mmsghdr *msgs, unsigned int n,
int flags, struct timespec *timeout)
{
return emulate_recvmmsg(fd, msgs, n, flags, timeout);
}
#else
/* recvmmsg was redefined in lib/socket-util.c, should undef recvmmsg here
* to avoid recursion */
#undef recvmmsg
int
wrap_recvmmsg(int fd, struct mmsghdr *msgs, unsigned int n,
int flags, struct timespec *timeout)
{
ovs_assert(!timeout); /* XXX not emulated */

static bool recvmmsg_broken = false;
if (!recvmmsg_broken) {
int save_errno = errno;
int retval = recvmmsg(fd, msgs, n, flags, timeout);
if (retval >= 0 || errno != ENOSYS) {
return retval;
}
recvmmsg_broken = true;
errno = save_errno;
}
return emulate_recvmmsg(fd, msgs, n, flags, timeout);
}
#endif
#endif
24 changes: 14 additions & 10 deletions lib/socket-util.h
Original file line number Diff line number Diff line change
Expand Up @@ -104,19 +104,20 @@ int make_unix_socket(int style, bool nonblock,
const char *bind_path, const char *connect_path);
int get_unix_name_len(const struct sockaddr_un *sun, socklen_t sun_len);

/* Universal sendmmsg support.
/* Universal sendmmsg and recvmmsg support.
*
* Some platforms, such as new enough Linux and FreeBSD, support sendmmsg, but
* other platforms (or older ones) do not. We add the following infrastructure
* to allow all code to use sendmmsg, regardless of platform support:
* Some platforms, such as new enough Linux and FreeBSD, support sendmmsg and
* recvmmsg, but other platforms (or older ones) do not. We add the following
* infrastructure to allow all code to use sendmmsg and recvmmsg, regardless of
* platform support:
*
* - For platforms that lack sendmmsg entirely, we emulate it.
* - For platforms that lack these functions entirely, we emulate them.
*
* - Some platforms have sendmmsg() in the C library but not in the kernel.
* For example, this is true if a Linux system has a newer glibc with an
* old kernel. To compensate, even if sendmmsg() appears to be available,
* we still wrap it with a handler that uses our emulation if sendmmsg()
* returns ENOSYS.
* - Some platforms have sendmmsg() and recvmmsg() in the C library but not in
* the kernel. For example, this is true if a Linux system has a newer glibc
* with an old kernel. To compensate, even if these functions appear to be
* available, we still wrap them with handlers that uses our emulation if the
* underlying function returns ENOSYS.
*/
#ifndef HAVE_STRUCT_MMSGHDR_MSG_LEN
struct mmsghdr {
Expand All @@ -126,9 +127,12 @@ struct mmsghdr {
#endif
#ifndef HAVE_SENDMMSG
int sendmmsg(int, struct mmsghdr *, unsigned int, unsigned int);
int recvmmsg(int, struct mmsghdr *, unsigned int, int, struct timespec *);
#else
#define sendmmsg wrap_sendmmsg
int wrap_sendmmsg(int, struct mmsghdr *, unsigned int, unsigned int);
#define recvmmsg wrap_recvmmsg
int wrap_recvmmsg(int, struct mmsghdr *, unsigned int, int, struct timespec *);
#endif

/* Helpers for calling ioctl() on an AF_INET socket. */
Expand Down

0 comments on commit b901898

Please sign in to comment.