linux: Add time64 select support
authorAdhemerval Zanella <adhemerval.zanella@linaro.org>
Mon, 6 Jul 2020 19:06:51 +0000 (16:06 -0300)
committerAdhemerval Zanella <adhemerval.zanella@linaro.org>
Mon, 28 Sep 2020 19:21:48 +0000 (16:21 -0300)
The syscall __NR_pselect6_time64 (32-bit) or __NR_pselect6 (64-bit)
is used as default.  For architectures with __ASSUME_TIME64_SYSCALLS
the 32-bit fallback uses __NR_select/__NR__newselect or __NR_pselect6
(it should cover the microblaze case where older kernels do not
provide __NR_pselect6).

Checked on x86_64-linux-gnu and i686-linux-gnu (on 5.4 and on 4.15
kernel).

Reviewed-by: Alistair Francis <alistair.francis@wdc.com>
include/sys/select.h
include/time.h
sysdeps/unix/sysv/linux/select.c

index b5ae9af..ec073de 100644 (file)
@@ -5,8 +5,11 @@
 /* Now define the internal interfaces.  */
 # if __TIMESIZE == 64
 #  define __pselect64 __pselect
+#  define __select64  __select
 #else
 # include <struct___timespec64.h>
+# include <struct___timeval64.h>
+
 extern int __pselect64 (int __nfds, fd_set *__readfds,
                        fd_set *__writefds, fd_set *__exceptfds,
                        const struct __timespec64 *__timeout,
@@ -18,6 +21,11 @@ extern int __pselect32 (int __nfds, fd_set *__readfds,
                        const struct __timespec64 *__timeout,
                        const __sigset_t *__sigmask)
   attribute_hidden;
+
+extern int __select64 (int __nfds, fd_set *__readfds,
+                      fd_set *__writefds, fd_set *__exceptfds,
+                      struct __timeval64 *__timeout);
+libc_hidden_proto (__select64)
 #endif
 extern int __pselect (int __nfds, fd_set *__readfds,
                      fd_set *__writefds, fd_set *__exceptfds,
index fe4da9c..936486e 100644 (file)
@@ -464,6 +464,12 @@ valid_timespec_to_timeval32 (const struct timespec ts)
   return (struct __timeval32) { (time_t) ts.tv_sec, ts.tv_nsec / 1000 };
 }
 
+static inline struct __timeval64
+valid_timespec_to_timeval64 (const struct timespec ts)
+{
+  return (struct __timeval64) { (time_t) ts.tv_sec, ts.tv_nsec / 1000 };
+}
+
 /* Check if a value is in the valid nanoseconds range. Return true if
    it is, false otherwise.  */
 static inline bool
index 54c50ed..bed52fb 100644 (file)
@@ -21,6 +21,7 @@
 #include <sys/select.h>
 #include <errno.h>
 #include <sysdep-cancel.h>
+#include <time64-support.h>
 
 /* Check the first NFDS descriptors each in READFDS (if not NULL) for read
    readiness, in WRITEFDS (if not NULL) for write readiness, and in EXCEPTFDS
    after waiting the interval specified therein.  Returns the number of ready
    descriptors, or -1 for errors.  */
 
-#ifdef __NR__newselect
-# undef __NR_select
-# define __NR_select __NR__newselect
-#endif
-
 int
-__select (int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
-         struct timeval *timeout)
+__select64 (int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
+           struct __timeval64 *timeout)
 {
-#ifdef __NR_select
-  return SYSCALL_CANCEL (select, nfds, readfds, writefds, exceptfds,
-                        timeout);
-#else
-  int result;
-  struct timespec ts, *tsp = NULL;
-
-  if (timeout)
+  struct __timespec64 ts64, *pts64 = NULL;
+  if (timeout != NULL)
     {
-      TIMEVAL_TO_TIMESPEC (timeout, &ts);
-      tsp = &ts;
+      ts64 = timeval64_to_timespec64 (*timeout);
+      pts64 = &ts64;
     }
 
-  result = SYSCALL_CANCEL (pselect6, nfds, readfds, writefds, exceptfds, tsp,
-                          NULL);
-
-  if (timeout)
+#ifndef __NR_pselect6_time64
+# define __NR_pselect6_time64 __NR_pselect6
+#endif
+  int r;
+  if (supports_time64 ())
     {
+      r = SYSCALL_CANCEL (pselect6_time64, nfds, readfds, writefds, exceptfds,
+                         pts64, NULL);
       /* Linux by default will update the timeout after a pselect6 syscall
          (though the pselect() glibc call suppresses this behavior).
          Since select() on Linux has the same behavior as the pselect6
          syscall, we update the timeout here.  */
-      TIMESPEC_TO_TIMEVAL (timeout, &ts);
+      if (r == 0 || errno != ENOSYS)
+       {
+         if (timeout != NULL)
+           TIMEVAL_TO_TIMESPEC (timeout, &ts64);
+         return r;
+       }
+
+      mark_time64_unsupported ();
     }
 
-  return result;
+#ifndef __ASSUME_TIME64_SYSCALLS
+  struct timespec ts32, *pts32 = NULL;
+  if (timeout != NULL)
+    {
+      if (! in_time_t_range (timeout->tv_sec))
+       {
+         __set_errno (EINVAL);
+         return -1;
+       }
+      ts32 = valid_timespec64_to_timespec (ts64);
+      pts32 = &ts32;
+    }
+# ifndef __ASSUME_PSELECT
+#  ifdef __NR__newselect
+#   undef __NR_select
+#   define __NR_select __NR__newselect
+#  endif
+  r = SYSCALL_CANCEL (select, nfds, readfds, writefds, exceptfds, pts32);
+# else
+  r = SYSCALL_CANCEL (pselect6, nfds, readfds, writefds, exceptfds, pts32,
+                     NULL);
+# endif
+  if (r >= 0 && timeout != NULL)
+    *timeout = valid_timespec_to_timeval64 (ts32);
 #endif
+
+  return r;
 }
+
+#if __TIMESIZE != 64
+libc_hidden_def (__select64)
+
+int
+__select (int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
+         struct timeval *timeout)
+{
+  struct __timeval64 tv64, *ptv64 = NULL;
+  if (timeout != NULL)
+    {
+      tv64 = valid_timeval_to_timeval64 (*timeout);
+      ptv64 = &tv64;
+    }
+  int r = __select64 (nfds, readfds, writefds, exceptfds, ptv64);
+  if (r >= 0 && timeout != NULL)
+    /* The remanining timeout will be always less the input TIMEOUT.  */
+    *timeout = valid_timeval64_to_timeval (tv64);
+  return r;
+}
+#endif
 libc_hidden_def (__select)
 
 weak_alias (__select, select)