From 5e09d6036ed8ad0aed641cdff7938142c643c78c Mon Sep 17 00:00:00 2001 From: "Albert ARIBAUD (3ADEV)" Date: Tue, 20 Jun 2017 23:37:11 +0200 Subject: Y2038: implement Y2038-proof clock_gettime, clock_settime, and clock_getres --- include/time.h | 8 +++ sysdeps/posix/clock_getres.c | 87 +++++++++++++++++++++++++++++++- sysdeps/unix/clock_gettime.c | 46 +++++++++++++++++ sysdeps/unix/clock_settime.c | 56 +++++++++++++++++++- sysdeps/unix/sysv/linux/arm/Versions | 4 ++ sysdeps/unix/sysv/linux/arm/init-first.c | 15 ++++++ sysdeps/unix/sysv/linux/arm/libc-vdso.h | 1 + sysdeps/unix/sysv/linux/clock_getres.c | 40 +++++++++++++++ sysdeps/unix/sysv/linux/clock_gettime.c | 44 ++++++++++++++++ sysdeps/unix/sysv/linux/clock_settime.c | 23 +++++++++ time/time.h | 24 +++++++++ 11 files changed, 346 insertions(+), 2 deletions(-) diff --git a/include/time.h b/include/time.h index 3f4a4c3f5a..9b29235870 100644 --- a/include/time.h +++ b/include/time.h @@ -22,6 +22,11 @@ libc_hidden_proto (localtime) libc_hidden_proto (strftime) libc_hidden_proto (strptime) +/* Indicates whether the underlying kernel has 64-bit time support. + This is required for e.g. librt, which cannot directly check the + flag variable that init-first.c sets when detecting support. */ +extern int __y2038_kernel_support (void); + #if BYTE_ORDER == BIG_ENDIAN struct __timespec64 { @@ -40,8 +45,11 @@ struct __timespec64 extern __typeof (clock_getres) __clock_getres; extern __typeof (clock_gettime) __clock_gettime; +extern int __clock_getres64 (clockid_t __clock_id, struct __timespec64 *__res) __THROW; libc_hidden_proto (__clock_gettime) +extern int __clock_gettime64 (clockid_t __clock_id, struct __timespec64 *__tp) __THROW; extern __typeof (clock_settime) __clock_settime; +extern int __clock_settime64 (clockid_t __clock_id, const struct __timespec64 *__tp) __THROW; extern __typeof (clock_nanosleep) __clock_nanosleep; extern __typeof (clock_getcpuclockid) __clock_getcpuclockid; diff --git a/sysdeps/posix/clock_getres.c b/sysdeps/posix/clock_getres.c index b6248bef2c..3b74014d41 100644 --- a/sysdeps/posix/clock_getres.c +++ b/sysdeps/posix/clock_getres.c @@ -23,7 +23,6 @@ #include #include - #if HP_TIMING_AVAIL static long int nsec; /* Clock frequency of the processor. */ @@ -53,6 +52,33 @@ hp_timing_getres (struct timespec *res) return 0; } + +static int +hp_timing_getres64 (struct __timespec64 *res) +{ + if (__glibc_unlikely (nsec == 0)) + { + hp_timing_t freq; + + /* This can only happen if we haven't initialized the `nsec' + variable yet. Do this now. We don't have to protect this + code against multiple execution since all of them should + lead to the same result. */ + freq = __get_clockfreq (); + if (__glibc_unlikely (freq == 0)) + /* Something went wrong. */ + return -1; + + nsec = MAX (UINT64_C (1000000000) / freq, 1); + } + + /* Fill in the values. + The seconds are always zero (unless we have a 1Hz machine). */ + res->tv_sec = 0; + res->tv_nsec = nsec; + + return 0; +} #endif static inline int @@ -73,6 +99,24 @@ realtime_getres (struct timespec *res) return -1; } +static inline int +realtime_getres64 (struct __timespec64 *res) +{ + long int clk_tck = sysconf (_SC_CLK_TCK); + + if (__glibc_likely (clk_tck != -1)) + { + /* This implementation assumes that the realtime clock has a + resolution higher than 1 second. This is the case for any + reasonable implementation. */ + res->tv_sec = 0; + res->tv_nsec = 1000000000 / clk_tck; + return 0; + } + + return -1; +} + /* Get resolution of clock. */ int @@ -116,3 +160,44 @@ __clock_getres (clockid_t clock_id, struct timespec *res) return retval; } weak_alias (__clock_getres, clock_getres) + +int +__clock_getres64 (clockid_t clock_id, struct __timespec64 *res) +{ + int retval = -1; + + switch (clock_id) + { +#ifdef SYSDEP_GETRES64 + SYSDEP_GETRES64; +#endif + +#ifndef HANDLED_REALTIME64 + case CLOCK_REALTIME64: + retval = realtime_getres64 (res); + break; +#endif /* handled REALTIME */ + + default: +#ifdef SYSDEP_GETRES_CPU64 + SYSDEP_GETRES_CPU64; +#endif +#if HP_TIMING_AVAIL + if ((clock_id & ((1 << CLOCK_IDFIELD_SIZE) - 1)) + == CLOCK_THREAD_CPUTIME_ID) + retval = hp_timing_getres64 (res); + else +#endif + __set_errno (EINVAL); + break; + +#if HP_TIMING_AVAIL && !defined HANDLED_CPUTIME + case CLOCK_PROCESS_CPUTIME_ID: + case CLOCK_THREAD_CPUTIME_ID: + retval = hp_timing_getres64 (res); + break; +#endif + } + + return retval; +} diff --git a/sysdeps/unix/clock_gettime.c b/sysdeps/unix/clock_gettime.c index 5262066f51..ac224c919b 100644 --- a/sysdeps/unix/clock_gettime.c +++ b/sysdeps/unix/clock_gettime.c @@ -134,3 +134,49 @@ __clock_gettime (clockid_t clock_id, struct timespec *tp) } weak_alias (__clock_gettime, clock_gettime) libc_hidden_def (__clock_gettime) + +/* Get current value of CLOCK and store it in TP, 64-bit version. */ +int +__clock_gettime64 (clockid_t clock_id, struct __timespec64 *tp) +{ + int retval = -1; + + switch (clock_id) + { +#ifdef SYSDEP_GETTIME64 + SYSDEP_GETTIME64; +#endif + +#ifndef HANDLED_REALTIME + case CLOCK_REALTIME: + { + struct timeval tv; + retval = gettimeofday (&tv, NULL); + if (retval == 0) + TIMEVAL_TO_TIMESPEC (&tv, tp); + } + break; +#endif + + default: +#ifdef SYSDEP_GETTIME64_CPU + SYSDEP_GETTIME64_CPU (clock_id, tp); +#endif +#if HP_TIMING_AVAIL + if ((clock_id & ((1 << CLOCK_IDFIELD_SIZE) - 1)) + == CLOCK_THREAD_CPUTIME_ID) + retval = hp_timing_gettime (clock_id, tp); + else +#endif + __set_errno (EINVAL); + break; + +#if HP_TIMING_AVAIL && !defined HANDLED_CPUTIME + case CLOCK_PROCESS_CPUTIME_ID: + retval = hp_timing_gettime (clock_id, tp); + break; +#endif + } + + return retval; +} diff --git a/sysdeps/unix/clock_settime.c b/sysdeps/unix/clock_settime.c index 957a4b1599..3c085580de 100644 --- a/sysdeps/unix/clock_settime.c +++ b/sysdeps/unix/clock_settime.c @@ -69,8 +69,62 @@ hp_timing_settime (clockid_t clock_id, const struct timespec *tp) } #endif +/* Set CLOCK to value TP, 64-bit Y2038-safe version. */ +int +__clock_settime64 (clockid_t clock_id, const struct __timespec64 *tp) +{ + int retval = EOVERFLOW; + + /* Make sure the time cvalue is OK. */ + if (tp->tv_nsec < 0 || tp->tv_nsec >= 1000000000) + { + __set_errno (EINVAL); + return -1; + } + + switch (clock_id) + { +#define HANDLE_REALTIME \ + do { \ + struct timeval tv; \ + TIMESPEC_TO_TIMEVAL (&tv, tp); \ + \ + retval = settimeofday (&tv, NULL); \ + } while (0) + +#ifdef SYSDEP_SETTIME64 + SYSDEP_SETTIME64; +#endif + +#ifndef HANDLED_REALTIME + case CLOCK_REALTIME: + HANDLE_REALTIME; + break; +#endif + + default: +#ifdef SYSDEP_SETTIME64_CPU + SYSDEP_SETTIME64_CPU; +#endif +#ifndef HANDLED_CPUTIME +# if HP_TIMING_AVAIL + if (CPUCLOCK_WHICH (clock_id) == CLOCK_PROCESS_CPUTIME_ID + || CPUCLOCK_WHICH (clock_id) == CLOCK_THREAD_CPUTIME_ID) + retval = hp_timing_settime (clock_id, tp); + else +# endif + { + __set_errno (EINVAL); + retval = -1; + } +#endif + break; + } + + return retval; +} -/* Set CLOCK to value TP. */ +/* Set CLOCK to value TP, 64-bit Y2038-unsafe version. */ int __clock_settime (clockid_t clock_id, const struct timespec *tp) { diff --git a/sysdeps/unix/sysv/linux/arm/Versions b/sysdeps/unix/sysv/linux/arm/Versions index f7feda3650..e3cd16ddef 100644 --- a/sysdeps/unix/sysv/linux/arm/Versions +++ b/sysdeps/unix/sysv/linux/arm/Versions @@ -15,6 +15,8 @@ libc { __default_sa_restorer; __default_rt_sa_restorer; # nptl/pthread_cond_timedwait.c uses INTERNAL_VSYSCALL(clock_gettime). __vdso_clock_gettime; + # __y2038_kernel_support is used by e.g. librt + __y2038_kernel_support; } # Y2038 symbols are given their own version until they can be put in @@ -27,5 +29,7 @@ libc { __localtime64; __localtime64_r; __mktime64; __timelocal64_r; __timegm64; + __clock_gettime64; __clock_settime64; __clock_getres64; + __vdso_clock_gettime64; } } diff --git a/sysdeps/unix/sysv/linux/arm/init-first.c b/sysdeps/unix/sysv/linux/arm/init-first.c index 3c289c2a25..61c01a56a7 100644 --- a/sysdeps/unix/sysv/linux/arm/init-first.c +++ b/sysdeps/unix/sysv/linux/arm/init-first.c @@ -23,6 +23,14 @@ int (*VDSO_SYMBOL(gettimeofday)) (struct timeval *, void *) attribute_hidden; int (*VDSO_SYMBOL(clock_gettime)) (clockid_t, struct timespec *); +long (*VDSO_SYMBOL(clock_gettime64)) (clockid_t, struct __timespec64 *); + +int __y2038_linux_support; + +int __y2038_kernel_support (void) +{ + return __y2038_linux_support; +} static inline void _libc_vdso_platform_setup (void) @@ -36,6 +44,13 @@ _libc_vdso_platform_setup (void) p = _dl_vdso_vsym ("__vdso_clock_gettime", &linux26); PTR_MANGLE (p); VDSO_SYMBOL (clock_gettime) = p; + + /* (aaribaud) TODO: map to version where clock_gettime64 officially appears */ + p = _dl_vdso_vsym ("__vdso_clock_gettime64", NULL); + PTR_MANGLE (p); + VDSO_SYMBOL (clock_gettime64) = p; + + __y2038_linux_support = (p != NULL) ? 1 : 0; } # define VDSO_SETUP _libc_vdso_platform_setup diff --git a/sysdeps/unix/sysv/linux/arm/libc-vdso.h b/sysdeps/unix/sysv/linux/arm/libc-vdso.h index ae37b574e1..ee0e5941c0 100644 --- a/sysdeps/unix/sysv/linux/arm/libc-vdso.h +++ b/sysdeps/unix/sysv/linux/arm/libc-vdso.h @@ -27,6 +27,7 @@ extern int (*VDSO_SYMBOL(gettimeofday)) (struct timeval *, void *) attribute_hidden; extern int (*VDSO_SYMBOL(clock_gettime)) (clockid_t, struct timespec *); +extern long (*VDSO_SYMBOL(clock_gettime64)) (clockid_t, struct __timespec64 *); #endif diff --git a/sysdeps/unix/sysv/linux/clock_getres.c b/sysdeps/unix/sysv/linux/clock_getres.c index 2b7bb65e55..3da8c0ef1e 100644 --- a/sysdeps/unix/sysv/linux/clock_getres.c +++ b/sysdeps/unix/sysv/linux/clock_getres.c @@ -48,4 +48,44 @@ #define SYSDEP_GETRES_CPU SYSCALL_GETRES #define SYSDEP_GETRES_CPUTIME /* Default catches them too. */ +/* The 64-bit version */ + +extern int __y2038_linux_support; + +#define SYSCALL_GETRES64 \ + if (__y2038_linux_support) \ + { \ + retval = INLINE_VSYSCALL (clock_getres64, 2, clock_id, res); \ + } \ + else \ + { \ + retval = INLINE_VSYSCALL (clock_getres, 2, clock_id, &ts32); \ + if (retval==0) \ + { \ + res->tv_sec = ts32.tv_sec; \ + res->tv_nsec = ts32.tv_nsec; \ + res->tv_pad = 0; \ + } \ + } \ + break + +/* The REALTIME and MONOTONIC clock are definitely supported in the + kernel. */ +#define SYSDEP_GETRES64 \ + SYSDEP_GETRES_CPUTIME64 \ + case CLOCK_REALTIME: \ + case CLOCK_MONOTONIC: \ + case CLOCK_MONOTONIC_RAW: \ + case CLOCK_REALTIME_COARSE: \ + case CLOCK_MONOTONIC_COARSE: \ + SYSCALL_GETRES64 + +/* We handled the REALTIME clock here. */ +#define HANDLED_REALTIME64 1 +#define HANDLED_CPUTIME64 1 + +#define SYSDEP_GETRES_CPU64 SYSCALL_GETRES64 +#define SYSDEP_GETRES_CPUTIME64 \ + struct timespec ts32; + #include diff --git a/sysdeps/unix/sysv/linux/clock_gettime.c b/sysdeps/unix/sysv/linux/clock_gettime.c index e232f69a4f..30d3e12e03 100644 --- a/sysdeps/unix/sysv/linux/clock_gettime.c +++ b/sysdeps/unix/sysv/linux/clock_gettime.c @@ -44,4 +44,48 @@ break #define SYSDEP_GETTIME_CPUTIME /* Default catches them too. */ +/* 64-bit versions */ + +/* The REALTIME and MONOTONIC clock are definitely supported in the + kernel. */ +#define SYSDEP_GETTIME64 \ + SYSDEP_GETTIME64_CPUTIME; \ + case CLOCK_REALTIME: \ + case CLOCK_MONOTONIC: \ + if (__y2038_linux_support) \ + { \ + retval = INLINE_VSYSCALL (clock_gettime64, 2, clock_id, tp); \ + } \ + else \ + { \ + retval = INLINE_VSYSCALL (clock_gettime, 2, clock_id, &ts32); \ + if (retval==0) \ + { \ + tp->tv_sec = ts32.tv_sec; \ + tp->tv_nsec = ts32.tv_nsec; \ + tp->tv_pad = 0; \ + } \ + } \ + break + +#define SYSDEP_GETTIME64_CPU(clock_id, tp) \ + if (__y2038_linux_support) \ + { \ + retval = INLINE_VSYSCALL (clock_gettime64, 2, clock_id, tp); \ + } \ + else \ + { \ + retval = INLINE_VSYSCALL (clock_gettime, 2, clock_id, &ts32); \ + if (retval==0) \ + { \ + tp->tv_sec = ts32.tv_sec; \ + tp->tv_nsec = ts32.tv_nsec; \ + tp->tv_pad = 0; \ + } \ + } \ + break +#define SYSDEP_GETTIME64_CPUTIME \ + struct timespec ts32; \ + extern int __y2038_linux_support; + #include diff --git a/sysdeps/unix/sysv/linux/clock_settime.c b/sysdeps/unix/sysv/linux/clock_settime.c index 058c518e5a..282195626a 100644 --- a/sysdeps/unix/sysv/linux/clock_settime.c +++ b/sysdeps/unix/sysv/linux/clock_settime.c @@ -35,4 +35,27 @@ #define SYSDEP_SETTIME_CPU \ retval = INLINE_SYSCALL (clock_settime, 2, clock_id, tp) +/* 64-bit time version */ + +extern int __y2038_linux_support; + +#define SYSDEP_SETTIME64 \ + case CLOCK_REALTIME: \ + if (__y2038_linux_support) \ + { \ + struct __timespec64 ts64; \ + ts64.tv_sec = tp->tv_sec; \ + ts64.tv_nsec = tp->tv_nsec; \ + ts64.tv_pad = 0; \ + retval = INLINE_SYSCALL (clock_settime64, 2, clock_id, &ts64); \ + } \ + else if (tp->tv_sec <= INT_MAX) \ + { \ + struct timespec ts32; \ + ts32.tv_sec = tp->tv_sec; \ + ts32.tv_nsec = tp->tv_nsec; \ + retval = INLINE_SYSCALL (clock_settime, 2, clock_id, &ts32); \ + } \ + break + #include diff --git a/time/time.h b/time/time.h index f89884d91b..07ad045e87 100644 --- a/time/time.h +++ b/time/time.h @@ -299,12 +299,36 @@ extern int nanosleep (const struct timespec *__requested_time, /* Get resolution of clock CLOCK_ID. */ +#ifdef __USE_TIME_BITS64 +# if defined(__REDIRECT) +extern int __REDIRECT (clock_getres, (clockid_t __clock_id, struct + timespec *__res), __clock_getres64) __THROW; +# else +# define clock_getres __clock_getres64 +# endif +#endif extern int clock_getres (clockid_t __clock_id, struct timespec *__res) __THROW; /* Get current value of clock CLOCK_ID and store it in TP. */ +#ifdef __USE_TIME_BITS64 +# if defined(__REDIRECT) +extern int __REDIRECT (clock_gettime, (clockid_t __clock_id, struct + timespec *__tp), __clock_gettime64) __THROW; +# else +# define clock_gettime __clock_gettime64 +# endif +#endif extern int clock_gettime (clockid_t __clock_id, struct timespec *__tp) __THROW; /* Set clock CLOCK_ID to value TP. */ +#ifdef __USE_TIME_BITS64 +# if defined(__REDIRECT) +extern int __REDIRECT (clock_settime, (clockid_t __clock_id, const struct + timespec *__tp), __clock_settime64) __THROW; +# else +# define clock_settime __clock_settime64 +# endif +#endif extern int clock_settime (clockid_t __clock_id, const struct timespec *__tp) __THROW; -- cgit v1.2.1