[v2,5/7] y2038: linux: Provide __clock_getres64 implementation

Message ID 20190429104613.16209-6-lukma@denx.de
State Superseded
Headers

Commit Message

Lukasz Majewski April 29, 2019, 10:46 a.m. UTC
  This patch provides new __clock_getres64 explicit 64 bit function for
getting the resolution (precision) of specified clock ID. Moreover, a
32 bit version - __clock_getres has been refactored to internally use
__clock_getres64.

The __clock_getres is now supposed to be used on 32 bit systems -
hence the necessary checks and conversion to 64 bit type. After this
change it is intrinsically Y2038 safe.

The new 64 bit syscall (clock_getres_time64) available from Linux
5.1+ has been used when applicable on 32 bit systems.

The __ASSUME_64BIT_TIME flag indicates if the Linux kernel provides 64 bit
version of clock_getres (i.e. clock_getres_time64). If defined - return
value is returned unconditionally. If not - the 32 bit version of this
syscall is executed instead.

When working on 32 bit systems without Y2038 time support the
clock_getres returns error when received tv_sec is wrong (i.e. overflowed).
Moreover, the correctness of tv_nsec is checked.

The execution path on 64 bit systems has not been changed or affected
in any way.

Tests:
- The code has been tested with x86_64/x86 (native compilation):
make PARALLELMFLAGS="-j8" && make xcheck PARALLELMFLAGS="-j8"

- Run specific tests on ARM/x86 32bit systems (qemu):
https://github.com/lmajewski/meta-y2038
and run tests:
https://github.com/lmajewski/y2038-tests/commits/master
on kernels with and without 64 bit time support.

No regressions were observed.

* include/time.h (__clock_getres64):
  Add __clock_getres alias according to __TIMESIZE define
* sysdeps/unix/sysv/linux/clock_getres.c (__clock_getres):
  Refactor this function to be used only on 32 bit machines as a
  wrapper on __clock_getres64.
* sysdeps/unix/sysv/linux/clock_getres.c (__clock_getres64): Add
* sysdeps/unix/sysv/linux/clock_getres.c (__clock_getres64):
  Use clock_getres_time64 kernel syscall (available from 5.1-rc1+
  Linux) by 32 bit Y2038 safe systems

---
Changes for v2:
- Add support for __ASSUME_64BIT_TIME flag when Linux kernel provides
  syscalls supporting 64 bit time on 32 bit systems
- Provide fallback to 32 bit version of clock_getres when
  clock_getres_time64 is not available
---
 include/time.h                         |  8 +++++++
 sysdeps/unix/sysv/linux/clock_getres.c | 39 +++++++++++++++++++++++++++++++++-
 2 files changed, 46 insertions(+), 1 deletion(-)
  

Patch

diff --git a/include/time.h b/include/time.h
index 7f08559072..2ec5e80d9e 100644
--- a/include/time.h
+++ b/include/time.h
@@ -140,6 +140,14 @@  extern int __clock_settime64 (clockid_t clock_id,
 libc_hidden_proto (__clock_settime64)
 #endif
 
+#if __TIMESIZE == 64
+# define __clock_getres64 __clock_getres
+#else
+extern int __clock_getres64 (clockid_t clock_id,
+                             struct __timespec64 *tp);
+libc_hidden_proto (__clock_getres64);
+#endif
+
 /* Compute the `struct tm' representation of T,
    offset OFFSET seconds east of UTC,
    and store year, yday, mon, mday, wday, hour, min, sec into *TP.
diff --git a/sysdeps/unix/sysv/linux/clock_getres.c b/sysdeps/unix/sysv/linux/clock_getres.c
index 24b2299938..48dca5fc09 100644
--- a/sysdeps/unix/sysv/linux/clock_getres.c
+++ b/sysdeps/unix/sysv/linux/clock_getres.c
@@ -28,8 +28,45 @@ 
 
 /* Get resolution of clock.  */
 int
-__clock_getres (clockid_t clock_id, struct timespec *res)
+__clock_getres64 (clockid_t clock_id, struct __timespec64 *res)
 {
+#if defined (__TIMESIZE) && __TIMESIZE != 64
+# ifdef __NR_clock_getres_time64
+  int ret = INLINE_VSYSCALL (clock_getres_time64, 2, clock_id, res);
+#  ifdef __ASSUME_64BIT_TIME
+  return ret;
+#  else
+  if (ret == 0 || errno != ENOSYS)
+    /* Preserve non-error/non-ENOSYS return values.  */
+    return ret;
+#  endif
+# endif
+  struct timespec ts32;
+  int retval = INLINE_VSYSCALL (clock_getres, 2, clock_id, &ts32);
+  if (! retval)
+    valid_timespec_to_timespec64(&ts32, res);
+
+  return retval;
+#else
   return INLINE_VSYSCALL (clock_getres, 2, clock_id, res);
+#endif
 }
 weak_alias (__clock_getres, clock_getres)
+
+#if __TIMESIZE != 64
+int
+__clock_getres (clockid_t clock_id, struct timespec *res)
+{
+  struct __timespec64 ts64;
+  int retval;
+
+  retval = __clock_getres64 (clock_id, &ts64);
+  if (! retval && res && ! timespec64_to_timespec (&ts64, res))
+    {
+      __set_errno (EOVERFLOW);
+      return -1;
+    }
+
+  return retval;
+}
+#endif