recvmmsg: add time64 syscall support, decouple 32-bit time_t
authorRich Felker <dalias@aerifal.cx>
Tue, 30 Jul 2019 01:03:01 +0000 (21:03 -0400)
committerRich Felker <dalias@aerifal.cx>
Tue, 30 Jul 2019 01:03:01 +0000 (21:03 -0400)
the time64 syscall is used only if the timeout does not fit in 32
bits. after preprocessing, the code is unchanged on 64-bit archs. for
32-bit archs, the timeout now goes through an intermediate copy,
meaning that the caller does not get back the updated timeout. this is
based on my reading of the documentation, which does not document the
updating as a contract you can rely on, and mentions that the whole
recvmmsg timeout mechanism is buggy and unlikely to be useful. if it
turns out that there's interest in making the remaining time
officially available to callers, such functionality could be added
back later.

src/network/recvmmsg.c

index 58b1b2f635bdeba487f361acc9f0cc3a75e018aa..1dc67932dafc430c3eb9577c0b3431bb91854ca8 100644 (file)
@@ -1,8 +1,13 @@
 #define _GNU_SOURCE
 #include <sys/socket.h>
 #include <limits.h>
+#include <errno.h>
+#include <time.h>
 #include "syscall.h"
 
+#define IS32BIT(x) !((x)+0x80000000ULL>>32)
+#define CLAMP(x) (int)(IS32BIT(x) ? (x) : 0x7fffffffU+((0ULL+(x))>>63))
+
 int recvmmsg(int fd, struct mmsghdr *msgvec, unsigned int vlen, unsigned int flags, struct timespec *timeout)
 {
 #if LONG_MAX > INT_MAX
@@ -11,5 +16,18 @@ int recvmmsg(int fd, struct mmsghdr *msgvec, unsigned int vlen, unsigned int fla
        for (i = vlen; i; i--, mh++)
                mh->msg_hdr.__pad1 = mh->msg_hdr.__pad2 = 0;
 #endif
+#ifdef SYS_recvmmsg_time64
+       time_t s = timeout ? timeout->tv_sec : 0;
+       long ns = timeout ? timeout->tv_nsec : 0;
+       int r = -ENOSYS;
+       if (SYS_recvmmsg == SYS_recvmmsg_time64 || !IS32BIT(s))
+               r = __syscall_cp(SYS_recvmmsg_time64, fd, msgvec, vlen, flags,
+                       ((long long[]){s, ns}));
+       if (SYS_recvmmsg == SYS_recvmmsg_time64 || r!=-ENOSYS)
+               return __syscall_ret(r);
+       return syscall_cp(SYS_recvmmsg, fd, msgvec, vlen, flags,
+               timeout ? ((long[]){CLAMP(s), ns}) : 0);
+#else
        return syscall_cp(SYS_recvmmsg, fd, msgvec, vlen, flags, timeout);
+#endif
 }