[v3] sysdeps/nanosleep: Use clock_nanosleep_time64 if avaliable

Message ID 20191101095707.6208-1-alistair.francis@wdc.com
State New, archived
Headers

Commit Message

Alistair Francis Nov. 1, 2019, 9:57 a.m. UTC
  The nanosleep syscall is not supported on newer 32-bit platforms (such
as RV32). To fix this issue let's use clock_nanosleep_time64 if it is
avaliable.

Let's use CLOCK_REALTIME when calling clock_nanosleep_time64 as the
Linux specification says:
  "POSIX.1 specifies that nanosleep() should measure time against the
   CLOCK_REALTIME clock. However, Linux measures the time using the
   CLOCK_MONOTONIC clock. This probably does not matter, since the POSIX.1
   specification for clock_settime(2) says that discontinuous changes in
   CLOCK_REALTIME should not affect nanosleep()"
---
The nptl/check-abi-libpthread test is failing on a large number of
archs. This is what seems to be failing, although I can't figure out
why:

diff -p -U 0 ./sysdeps/unix/sysv/linux/mips/mips64/libpthread.abilist
/glibc-build-many/build/glibcs/mips64el-linux-gnu-n32-soft/glibc/nptl/libpthread.symlist
--- ./sysdeps/unix/sysv/linux/mips/mips64/libpthread.abilist
2019-10-23 15:15:14.834550013 -0700
+++
/glibc-build-many/build/glibcs/mips64el-linux-gnu-n32-soft/glibc/nptl/libpthread.symlist
2019-11-01 02:30:26.339004183 -0700
@@ -45 +44,0 @@ GLIBC_2.0 msync F
-GLIBC_2.0 nanosleep F
@@ -195 +193,0 @@ GLIBC_2.2.3 pthread_getattr_np F
-GLIBC_2.2.6 __nanosleep F

v3:
 - Fix incorrect fallthroughs
 - Call __clock_nanosleep_time64 to remove redundant code
 - Remove Linux version of nanosleep
v2:
 - Explicitly include `#include <kernel-features.h>`

 include/time.h                            | 22 ++++++++
 nptl/Makefile                             |  2 +-
 nptl/thrd_sleep.c                         | 25 ++++-----
 posix/nanosleep.c                         | 11 +++-
 sysdeps/unix/sysv/linux/clock_nanosleep.c | 67 +++++++++++++++++++++--
 sysdeps/unix/sysv/linux/nanosleep.c       | 31 -----------
 6 files changed, 103 insertions(+), 55 deletions(-)
 delete mode 100644 sysdeps/unix/sysv/linux/nanosleep.c
  

Comments

Joseph Myers Nov. 1, 2019, 5:14 p.m. UTC | #1
On Fri, 1 Nov 2019, Alistair Francis wrote:

> The nptl/check-abi-libpthread test is failing on a large number of
> archs. This is what seems to be failing, although I can't figure out
> why:

Because you're removing nanosleep from pthread-compat-wrappers.

If you want to remove a symbol from libpthread that is duplicated in libc, 
you need to make sure that, for *every* glibc configuration[*], the symbol 
versions of that symbol in libpthread are a subset of the symbol versions 
of that symbol in libc.  If that is the case, you can remove the symbol 
from libpthread, updating all the ABI test baselines at the same time. See 
many patches of Florian's for examples of moving a symbol from libpthread 
to libc.  If some symbol version isn't already in libc, it needs to be 
added there (see the move of clock_*, for example).

I think such changes to remove duplicate function definitions from 
libpthread are to be encouraged, now that it's possible to move a 
versioned symbol (from another library to libc) that way.  But they should 
*not* be mixed in with other changes to how the function is implemented, 
and you do need to check that all required symbol versions are already in 
libc as described above.  If moving two symbols at once (as in this patch 
which affects both nanosleep and __nanosleep) you need to make such a 
check for both symbols.


[*] That is, every configuration where you're removing the symbol from 
libpthread.  If your change is only for NPTL, you don't need to be 
concerned with Hurd.
  
Florian Weimer Nov. 2, 2019, 9:35 p.m. UTC | #2
* Joseph Myers:

> On Fri, 1 Nov 2019, Alistair Francis wrote:
>
>> The nptl/check-abi-libpthread test is failing on a large number of
>> archs. This is what seems to be failing, although I can't figure out
>> why:
>
> Because you're removing nanosleep from pthread-compat-wrappers.
>
> If you want to remove a symbol from libpthread that is duplicated in libc, 
> you need to make sure that, for *every* glibc configuration[*], the symbol 
> versions of that symbol in libpthread are a subset of the symbol versions 
> of that symbol in libc.  If that is the case, you can remove the symbol 
> from libpthread, updating all the ABI test baselines at the same time. See 
> many patches of Florian's for examples of moving a symbol from libpthread 
> to libc.  If some symbol version isn't already in libc, it needs to be 
> added there (see the move of clock_*, for example).

And it has to be added at the version currently under development,
otherwise applications which link against that symbol will encounter
various obscure failures when running on older glibc versions, rather
than the (now standard) missing symbol version failure.

> I think such changes to remove duplicate function definitions from 
> libpthread are to be encouraged, now that it's possible to move a 
> versioned symbol (from another library to libc) that way.  But they should 
> *not* be mixed in with other changes to how the function is implemented, 
> and you do need to check that all required symbol versions are already in 
> libc as described above.  If moving two symbols at once (as in this patch 
> which affects both nanosleep and __nanosleep) you need to make such a 
> check for both symbols.

I agree.
  

Patch

diff --git a/include/time.h b/include/time.h
index 000672e3bcc..310e0ff29fd 100644
--- a/include/time.h
+++ b/include/time.h
@@ -165,6 +165,8 @@  extern struct tm *__tz_convert (__time64_t timer, int use_localtime,
 extern int __nanosleep (const struct timespec *__requested_time,
 			struct timespec *__remaining);
 hidden_proto (__nanosleep)
+extern int __clock_nanosleep (clockid_t clock_id, int flags,
+                              const struct timespec *req, struct timespec *rem);
 extern int __getdate_r (const char *__string, struct tm *__resbufp)
   attribute_hidden;
 
@@ -187,6 +189,26 @@  libc_hidden_proto (__difftime64)
 
 extern double __difftime (time_t time1, time_t time0);
 
+#if __TIMESIZE == 64
+# define __thrd_sleep_time64 thrd_sleep
+# define __clock_nanosleep_time64 __clock_nanosleep
+# define __nanosleep_time64 __nanosleep
+# define __nanosleep_nocancel_time64 __nanosleep_nocancel
+#else
+extern int __thrd_sleep_time64 (const struct __timespec64* time_point,
+                                struct __timespec64* remaining);
+libc_hidden_proto (__thrd_sleep_time64)
+extern int __clock_nanosleep_time64 (clockid_t clock_id,
+                                     int flags, const struct __timespec64 *req,
+                                     struct __timespec64 *rem);
+libc_hidden_proto (__clock_nanosleep_time64)
+extern int __nanosleep_time64 (const struct __timespec64 *requested_time,
+                                struct __timespec64 *remaining);
+libc_hidden_proto (__nanosleep_time64)
+extern int __nanosleep_nocancel_time64 (const struct __timespec64 *requested_time,
+                                        struct __timespec64 *remaining);
+libc_hidden_proto (__nanosleep_nocancel_time64)
+#endif
 
 /* Use in the clock_* functions.  Size of the field representing the
    actual clock ID.  */
diff --git a/nptl/Makefile b/nptl/Makefile
index 41f8f5e8d23..206529063aa 100644
--- a/nptl/Makefile
+++ b/nptl/Makefile
@@ -44,7 +44,7 @@  pthread-compat-wrappers = \
 		      write read close accept \
 		      connect recv recvfrom send \
 		      sendto fsync lseek lseek64 \
-		      msync nanosleep open open64 pause \
+		      msync open open64 pause \
 		      pread pread64 pwrite pwrite64 \
 		      tcdrain wait waitpid msgrcv msgsnd \
 		      sigwait sigsuspend \
diff --git a/nptl/thrd_sleep.c b/nptl/thrd_sleep.c
index 2e185dd748e..c865f815eea 100644
--- a/nptl/thrd_sleep.c
+++ b/nptl/thrd_sleep.c
@@ -17,23 +17,18 @@ 
    <https://www.gnu.org/licenses/>.  */
 
 #include <time.h>
-#include <sysdep-cancel.h>
-
-#include "thrd_priv.h"
+#include <errno.h>
 
 int
 thrd_sleep (const struct timespec* time_point, struct timespec* remaining)
 {
-  INTERNAL_SYSCALL_DECL (err);
-  int ret = INTERNAL_SYSCALL_CANCEL (nanosleep, err, time_point, remaining);
-  if (INTERNAL_SYSCALL_ERROR_P (ret, err))
-    {
-      /* C11 states thrd_sleep function returns -1 if it has been interrupted
-	 by a signal, or a negative value if it fails.  */
-      ret = INTERNAL_SYSCALL_ERRNO (ret, err);
-      if (ret == EINTR)
-	return -1;
-      return -2;
-    }
-  return 0;
+  int ret = __clock_nanosleep (CLOCK_REALTIME, 0, time_point, remaining);
+  /* C11 states thrd_sleep function returns -1 if it has been interrupted
+     by a signal, or a negative value if it fails.  */
+  switch (ret)
+  {
+     case 0:      return 0;
+     case EINTR:  return -1;
+     default:     return -2;
+  }
 }
diff --git a/posix/nanosleep.c b/posix/nanosleep.c
index d8564c71192..12ee70a57c9 100644
--- a/posix/nanosleep.c
+++ b/posix/nanosleep.c
@@ -22,10 +22,15 @@ 
 /* Pause execution for a number of nanoseconds.  */
 int
 __nanosleep (const struct timespec *requested_time,
-	     struct timespec *remaining)
+             struct timespec *remaining)
 {
-  __set_errno (ENOSYS);
-  return -1;
+  int ret = __clock_nanosleep (CLOCK_REALTIME, 0, requested_time, remaining);
+  if (ret != 0)
+    {
+      __set_errno (ret);
+      return -1;
+    }
+  return ret;
 }
 stub_warning (nanosleep)
 
diff --git a/sysdeps/unix/sysv/linux/clock_nanosleep.c b/sysdeps/unix/sysv/linux/clock_nanosleep.c
index 1f240b8720a..97545e96a6c 100644
--- a/sysdeps/unix/sysv/linux/clock_nanosleep.c
+++ b/sysdeps/unix/sysv/linux/clock_nanosleep.c
@@ -16,6 +16,7 @@ 
    <https://www.gnu.org/licenses/>.  */
 
 #include <time.h>
+#include <kernel-features.h>
 #include <errno.h>
 
 #include <sysdep-cancel.h>
@@ -26,9 +27,11 @@ 
 /* We can simply use the syscall.  The CPU clocks are not supported
    with this function.  */
 int
-__clock_nanosleep (clockid_t clock_id, int flags, const struct timespec *req,
-		   struct timespec *rem)
+__clock_nanosleep_time64 (clockid_t clock_id, int flags, const struct __timespec64 *req,
+                          struct __timespec64 *rem)
 {
+  int r = -1;
+
   if (clock_id == CLOCK_THREAD_CPUTIME_ID)
     return EINVAL;
   if (clock_id == CLOCK_PROCESS_CPUTIME_ID)
@@ -37,12 +40,66 @@  __clock_nanosleep (clockid_t clock_id, int flags, const struct timespec *req,
   /* If the call is interrupted by a signal handler or encounters an error,
      it returns a positive value similar to errno.  */
   INTERNAL_SYSCALL_DECL (err);
-  int r = INTERNAL_SYSCALL_CANCEL (clock_nanosleep, err, clock_id, flags,
-				   req, rem);
+
+#ifdef __ASSUME_TIME64_SYSCALLS
+# ifndef __NR_clock_nanosleep_time64
+#  define __NR_clock_nanosleep_time64 __NR_clock_nanosleep
+# endif
+  r = INTERNAL_SYSCALL_CANCEL (clock_nanosleep_time64, err, clock_id,
+                               flags, req, rem);
+#else
+# ifdef __NR_clock_nanosleep_time64
+  long int ret_64;
+
+  ret_64 = INTERNAL_SYSCALL_CANCEL (clock_nanosleep_time64, err, clock_id,
+                                    flags, req, rem);
+
+  if (ret_64 == 0 || errno != ENOSYS)
+    {
+      return (INTERNAL_SYSCALL_ERROR_P (ret_64, err)
+              ? INTERNAL_SYSCALL_ERRNO (ret_64, err) : 0);
+    }
+# endif /* __NR_clock_nanosleep_time64 */
+  struct timespec ts32, tr32;
+
+  if (! in_time_t_range (req->tv_sec))
+    {
+      __set_errno (EOVERFLOW);
+      return -1;
+    }
+
+  ts32 = valid_timespec64_to_timespec (*req);
+  r =  INTERNAL_SYSCALL_CANCEL (clock_nanosleep, err, &ts32, &tr32);
+
+  if ((r == 0 || errno != ENOSYS) && rem)
+    *rem = valid_timespec_to_timespec64 (tr32);
+#endif /* __ASSUME_TIME64_SYSCALLS */
+
   return (INTERNAL_SYSCALL_ERROR_P (r, err)
-	  ? INTERNAL_SYSCALL_ERRNO (r, err) : 0);
+          ? INTERNAL_SYSCALL_ERRNO (r, err) : 0);
 }
 
+#if __TIMESIZE != 64
+int
+__clock_nanosleep (clockid_t clock_id, int flags, const struct timespec *req,
+                   struct timespec *rem)
+{
+  int r;
+  struct __timespec64 treq64, trem64;
+
+  treq64 = valid_timespec_to_timespec64 (*req);
+  r = __clock_nanosleep_time64 (clock_id, flags, &treq64, &trem64);
+
+  if (r == 0 || errno != ENOSYS)
+    {
+      if (rem)
+        *rem = valid_timespec64_to_timespec (trem64);
+    }
+
+  return r;
+}
+#endif
+
 versioned_symbol (libc, __clock_nanosleep, clock_nanosleep, GLIBC_2_17);
 /* clock_nanosleep moved to libc in version 2.17;
    old binaries may expect the symbol version it had in librt.  */
diff --git a/sysdeps/unix/sysv/linux/nanosleep.c b/sysdeps/unix/sysv/linux/nanosleep.c
deleted file mode 100644
index 6787909248f..00000000000
--- a/sysdeps/unix/sysv/linux/nanosleep.c
+++ /dev/null
@@ -1,31 +0,0 @@ 
-/* Linux nanosleep syscall implementation.
-   Copyright (C) 2017-2019 Free Software Foundation, Inc.
-   This file is part of the GNU C Library.
-
-   The GNU C Library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) any later version.
-
-   The GNU C Library is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Lesser General Public License for more details.
-
-   You should have received a copy of the GNU Lesser General Public
-   License along with the GNU C Library; if not, see
-   <https://www.gnu.org/licenses/>.  */
-
-#include <time.h>
-#include <sysdep-cancel.h>
-#include <not-cancel.h>
-
-/* Pause execution for a number of nanoseconds.  */
-int
-__nanosleep (const struct timespec *requested_time,
-	     struct timespec *remaining)
-{
-  return SYSCALL_CANCEL (nanosleep, requested_time, remaining);
-}
-hidden_def (__nanosleep)
-weak_alias (__nanosleep, nanosleep)