Posix compliant cpu clocks V6 [2/3]: Glibc patch

From: Christoph Lameter
Date: Fri Oct 01 2004 - 15:11:04 EST


The following patch makes glibc not provide the above clocks and use the
kernel clocks instead if either of the following condition is met:

1. __ASSUME_POSIX_TIMERS is set

2. A call to probe the posix function is made if the corresponding
__NR_clock_* is defined. If the call is successful then the kernel clocks
will be used. Otherwise glibc will fall back to its own implementation of
the clocks.

The clock_gettime clock_settime and clock_res calls will use the corresponding
system calls for other clocks than CLOCK_REALTIME, CLOCK_MONOTONIC,
CLOCK_THREAD_CPUTIME_ID and CLOCK_PROCESS_CPUTIME_ID.

Patch needs some additional testing....

Signed-off-by: Christoph Lameter <clameter@xxxxxxx>

Index: libc/sysdeps/unix/sysv/linux/clock_gettime.c
===================================================================
--- libc.orig/sysdeps/unix/sysv/linux/clock_gettime.c 2004-09-28 15:22:02.359949008 -0700
+++ libc/sysdeps/unix/sysv/linux/clock_gettime.c 2004-10-01 08:57:51.025894976 -0700
@@ -16,61 +16,174 @@
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307 USA. */

+#include <errno.h>
+#include <stdint.h>
+#include <time.h>
+#include <sys/time.h>
+#include <libc-internal.h>
#include <sysdep.h>
+#include <ldsodefs.h>

#include "kernel-features.h"


#ifdef __ASSUME_POSIX_TIMERS
-/* This means the REALTIME and MONOTONIC clock are definitely
- supported in the kernel. */
-# define SYSDEP_GETTIME \
- case CLOCK_REALTIME: \
- case CLOCK_MONOTONIC: \
- retval = INLINE_SYSCALL (clock_gettime, 2, clock_id, tp); \
- break
-#elif defined __NR_clock_gettime
-/* Is the syscall known to exist? */
-int __libc_missing_posix_timers attribute_hidden;
-
-/* The REALTIME and MONOTONIC clock might be available. Try the
- syscall first. */
-# define SYSDEP_GETTIME \
- case CLOCK_REALTIME: \
- case CLOCK_MONOTONIC: \
- { \
- int e = EINVAL; \
- \
- if (!__libc_missing_posix_timers) \
- { \
- INTERNAL_SYSCALL_DECL (err); \
- int r = INTERNAL_SYSCALL (clock_gettime, err, 2, clock_id, tp); \
- if (!INTERNAL_SYSCALL_ERROR_P (r, err)) \
- { \
- retval = 0; \
- break; \
- } \
- \
- e = INTERNAL_SYSCALL_ERRNO (r, err); \
- if (e == ENOSYS) \
- { \
- __libc_missing_posix_timers = 1; \
- e = EINVAL; \
- } \
- } \
- \
- /* Fallback code. */ \
- if (e == EINVAL && clock_id == CLOCK_REALTIME) \
- HANDLE_REALTIME; \
- else \
- __set_errno (e); \
- } \
- break
+/* This means all clocks are definitely supported in the kernel. */
+int
+clock_gettime (clockid_t clock_id, struct timespec *tp)
+{
+ INTERNAL_SYSCALL_DECL (err);
+ int r = INTERNAL_SYSCALL (clock_gettime, err, 2, clock_id, tp);
+ if (!INTERNAL_SYSCALL_ERROR_P (r, err))
+ return 0;
+ return -1;
+}
+
+#else
+
+/*
+ * Need to deal with multiple complex fallback and legacy scenarios
+ *
+ * Give priority to the clock_gettime syscall but fall back if
+ * certain clocks are not available
+ * for CLOCK_REALTIME fall back to gettimeofday
+ * for CLOCK_PROCESS_CPUTIME_ID fall back to HP_TIMING
+ * for CLOCK_THREAD_CPUTIME_ID fall back to pthreads
+ */
+
+int __libc_missing_posix_stdtimers attribute_hidden;
+int __libc_missing_posix_cputimers attribute_hidden;
+
+int
+clock_gettime (clockid_t clock_id, struct timespec *tp)
+{
+ int retval = -1;
+
+ switch (clock_id)
+ {
+#if defined __NR_clock_gettime
+ case CLOCK_REALTIME: case CLOCK_MONOTONIC:
+ {
+ int e = EINVAL;
+
+ if (!__libc_missing_posix_stdtimers)
+ {
+ INTERNAL_SYSCALL_DECL (err);
+ int r = INTERNAL_SYSCALL (clock_gettime, err, 2, clock_id, tp);
+ if (!INTERNAL_SYSCALL_ERROR_P (r, err))
+ {
+ retval = 0;
+ break;
+ }
+
+ e = INTERNAL_SYSCALL_ERRNO (r, err);
+ if (e == ENOSYS)
+ {
+ __libc_missing_posix_stdtimers = 1;
+ e = EINVAL;
+ }
+ }
+
+ /* Fallback code. */
+ if (e != EINVAL || clock_id != CLOCK_REALTIME)
+ {
+ __set_errno (e);
+ break;
+ }
+ }
+ /* Fall through */
+#else
+ case CLOCK_REALTIME:
#endif
+ struct timeval tv;
+ retval = gettimeofday (&tv, NULL);
+ if (retval == 0)
+ /* Convert into `timespec'. */
+ TIMEVAL_TO_TIMESPEC (&tv, tp);
+ break;
+
+ case CLOCK_PROCESS_CPUTIME_ID: case CLOCK_THREAD_CPUTIME_ID:
+#if defined __NR_clock_gettime
+ if (!__libc_missing_posix_cputimers)
+ {
+ INTERNAL_SYSCALL_DECL (err);
+ int r = INTERNAL_SYSCALL (clock_gettime, err, 2, clock_id, tp);
+ if (!INTERNAL_SYSCALL_ERROR_P (r, err))
+ {
+ retval = 0;
+ break;
+ }
+
+ if (INTERNAL_SYSCALL_ERRNO (r, err) == ENOSYS)
+ __libc_missing_posix_cputimers = 1;
+ }
+
+#if HP_TIMING_AVAIL
+ /* Fallback code. */
+ {
+/* Clock frequency of the processor. We make it a 64-bit variable
+ because some jokers are already playing with processors with more
+ than 4GHz. */
+static hp_timing_t freq;
+
+
+/* This function is defined in the thread library. */
+extern int __pthread_clock_gettime (clockid_t clock_id, hp_timing_t freq,
+ struct timespec *tp)
+ __attribute__ ((__weak__));
+
+ hp_timing_t tsc;
+
+ if (__builtin_expect (freq == 0, 0))
+ {
+ /* This can only happen if we haven't initialized the `freq'
+ 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 (__builtin_expect (freq == 0, 0))
+ /* Something went wrong. */
+ break;
+ }
+
+ if (clock_id != CLOCK_PROCESS_CPUTIME_ID
+ && __pthread_clock_gettime != NULL)
+ {
+ retval = __pthread_clock_gettime (clock_id, freq, tp);
+ break;
+ }
+
+ /* Get the current counter. */
+ HP_TIMING_NOW (tsc);
+
+ /* Compute the offset since the start time of the process. */
+ tsc -= GL(dl_cpuclock_offset);
+
+ /* Compute the seconds. */
+ tp->tv_sec = tsc / freq;
+
+ /* And the nanoseconds. This computation should be stable until
+ we get machines with about 16GHz frequency. */
+ tp->tv_nsec = ((tsc % freq) * UINT64_C (1000000000)) / freq;
+
+ retval = 0;
+ }
+#endif
+#endif
+ break;
+
+ default:
+#if defined __NR_clock_gettime
+ INTERNAL_SYSCALL_DECL (err);
+ int r = INTERNAL_SYSCALL (clock_gettime, err, 2, clock_id, tp);
+ if (!INTERNAL_SYSCALL_ERROR_P (r, err))
+ retval = 0;
+#else
+ __set_errno(EINVAL);
+#endif
+ }
+ return retval;
+}

-#ifdef __NR_clock_gettime
-/* We handled the REALTIME clock here. */
-# define HANDLED_REALTIME 1
#endif

-#include <sysdeps/unix/clock_gettime.c>
Index: libc/sysdeps/unix/sysv/linux/clock_settime.c
===================================================================
--- libc.orig/sysdeps/unix/sysv/linux/clock_settime.c 2004-10-01 10:27:15.007445832 -0700
+++ libc/sysdeps/unix/sysv/linux/clock_settime.c 2004-10-01 11:09:40.938405496 -0700
@@ -1,4 +1,4 @@
-/* Copyright (C) 2003, 2004 Free Software Foundation, Inc.
+/* Copyright (C) 1999,2000,2001,2002,2003,2004 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
@@ -16,61 +16,160 @@
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307 USA. */

-#include <sysdep.h>
-
+#include <errno.h>
+#include <time.h>
+#include <sys/time.h>
+#include <libc-internal.h>
+#include <ldsodefs.h>
#include "kernel-features.h"

-
#ifdef __ASSUME_POSIX_TIMERS
-/* This means the REALTIME clock is definitely supported in the
- kernel. */
-# define SYSDEP_SETTIME \
- case CLOCK_REALTIME: \
- retval = INLINE_SYSCALL (clock_settime, 2, clock_id, tp); \
- break
-#elif defined __NR_clock_settime
-/* Is the syscall known to exist? */
-extern int __libc_missing_posix_timers attribute_hidden;
-
-/* The REALTIME clock might be available. Try the syscall first. */
-# define SYSDEP_SETTIME \
- case CLOCK_REALTIME: \
- { \
- int e = EINVAL; \
- \
- if (!__libc_missing_posix_timers) \
- { \
- INTERNAL_SYSCALL_DECL (err); \
- int r = INTERNAL_SYSCALL (clock_settime, err, 2, clock_id, tp); \
- if (!INTERNAL_SYSCALL_ERROR_P (r, err)) \
- { \
- retval = 0; \
- break; \
- } \
- \
- e = INTERNAL_SYSCALL_ERRNO (r, err); \
- if (e == ENOSYS) \
- { \
- __libc_missing_posix_timers = 1; \
- e = EINVAL; \
- } \
- } \
- \
- /* Fallback code. */ \
- if (e == EINVAL && clock_id == CLOCK_REALTIME) \
- HANDLE_REALTIME; \
- else \
- { \
- __set_errno (e); \
- retval = -1; \
- } \
- } \
- break
+/* This means all clocks are definitely supported in the kernel. */
+int
+clock_settime (clockid_t clock_id, struct timespec *tp)
+{
+ INTERNAL_SYSCALL_DECL (err);
+ int r = INTERNAL_SYSCALL (clock_settime, err, 2, clock_id, tp);
+ if (!INTERNAL_SYSCALL_ERROR_P (r, err))
+ return 0;
+ return -1;
+}
+
+#else
+/*
+ * Need to deal with multiple complex fallback and legacy scenarios
+ *
+ * Give priority to the clock_gettime syscall but fall back if
+ * certain clocks are not available
+ * for CLOCK_REALTIME fall back to gettimeofday
+ * for CLOCK_PROCESS_CPUTIME_ID fall back to HP_TIMING
+ * for CLOCK_THREAD_CPUTIME_ID fall back to pthreads
+ */
+
+int __libc_missing_posix_stdtimers attribute_hidden;
+int __libc_missing_posix_cputimers attribute_hidden;
+
+int
+clock_settime (clockid_t clock_id,const struct timespec *tp)
+{
+ int retval = -1;
+
+ switch (clock_id)
+ {
+#if defined __NR_clock_gettime
+ case CLOCK_REALTIME: case CLOCK_MONOTONIC:
+ {
+ int e = EINVAL;
+
+ if (!__libc_missing_posix_stdtimers)
+ {
+ INTERNAL_SYSCALL_DECL (err);
+ int r = INTERNAL_SYSCALL (clock_settime, err, 2, clock_id, tp);
+ if (!INTERNAL_SYSCALL_ERROR_P (r, err))
+ {
+ retval = 0;
+ break;
+ }
+
+ e = INTERNAL_SYSCALL_ERRNO (r, err);
+ if (e == ENOSYS)
+ {
+ __libc_missing_posix_stdtimers = 1;
+ e = EINVAL;
+ }
+ }
+
+ /* Fallback code. */
+ if (e != EINVAL || clock_id != CLOCK_REALTIME)
+ {
+ __set_errno (e);
+ break;
+ }
+ }
+ /* Fall through */
+#else
+ case CLOCK_REALTIME:
+#endif
+ struct timeval tv;
+ TIMESPEC_TO_TIMEVAL (&tv, tp);
+ retval = settimeofday (&tv, NULL);
+ break;
+
+ case CLOCK_PROCESS_CPUTIME_ID: case CLOCK_THREAD_CPUTIME_ID:
+#if defined __NR_clock_gettime
+ if (!__libc_missing_posix_cputimers)
+ {
+ INTERNAL_SYSCALL_DECL (err);
+ int r = INTERNAL_SYSCALL (clock_settime, err, 2, clock_id, tp);
+ if (!INTERNAL_SYSCALL_ERROR_P (r, err))
+ {
+ retval = 0;
+ break;
+ }
+
+ if (INTERNAL_SYSCALL_ERRNO (r, err) == ENOSYS)
+ __libc_missing_posix_cputimers = 1;
+ }
+
+#if HP_TIMING_AVAIL
+ /* Fallback code. */
+ {
+/* Clock frequency of the processor. We make it a 64-bit variable
+ because some jokers are already playing with processors with more
+ than 4GHz. */
+static hp_timing_t freq;
+
+
+/* This function is defined in the thread library. */
+extern int __pthread_clock_settime (clockid_t clock_id, hp_timing_t offset)
+ __attribute__ ((__weak__));
+
+ hp_timing_t tsc;
+ hp_timing_t usertime;
+
+ if (__builtin_expect (freq == 0, 0))
+ {
+ /* This can only happen if we haven't initialized the `freq'
+ 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 (__builtin_expect (freq == 0, 0))
+ /* Something went wrong. */
+ break;
+ }
+
+ /* Get the current counter. */
+ HP_TIMING_NOW (tsc);
+
+ /* Convert the user-provided time into CPU ticks. */
+ usertime = tp->tv_sec * freq + (tp->tv_nsec * freq) / 1000000000ull;
+
+ /* Determine the offset and use it as the new base value. */
+ if (clock_id == CLOCK_PROCESS_CPUTIME_ID
+ || __pthread_clock_settime == NULL)
+ GL(dl_cpuclock_offset) = tsc - usertime;
+ else
+ __pthread_clock_settime (clock_id, tsc - usertime);
+
+ retval = 0;
+ }
+#endif
+#endif
+ break;
+
+ default:
+#if defined __NR_clock_gettime
+ INTERNAL_SYSCALL_DECL (err);
+ int r = INTERNAL_SYSCALL (clock_gettime, err, 2, clock_id, tp);
+ if (!INTERNAL_SYSCALL_ERROR_P (r, err))
+ retval = 0;
+#else
+ __set_errno(EINVAL);
#endif
+ }
+ return retval;
+}

-#ifdef __NR_clock_settime
-/* We handled the REALTIME clock here. */
-# define HANDLED_REALTIME 1
#endif

-#include <sysdeps/unix/clock_settime.c>
Index: libc/sysdeps/unix/sysv/linux/clock_getres.c
===================================================================
--- libc.orig/sysdeps/unix/sysv/linux/clock_getres.c 2004-09-28 15:22:02.351950224 -0700
+++ libc/sysdeps/unix/sysv/linux/clock_getres.c 2004-10-01 12:23:50.099030272 -0700
@@ -16,61 +16,159 @@
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307 USA. */

+#include <errno.h>
+#include <stdint.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <libc-internal.h>
#include <sysdep.h>
+#include <ldsodefs.h>

#include "kernel-features.h"


#ifdef __ASSUME_POSIX_TIMERS
-/* This means the REALTIME and MONOTONIC clock are definitely
- supported in the kernel. */
-# define SYSDEP_GETRES \
- case CLOCK_REALTIME: \
- case CLOCK_MONOTONIC: \
- retval = INLINE_SYSCALL (clock_getres, 2, clock_id, res); \
- break
-#elif defined __NR_clock_getres
-/* Is the syscall known to exist? */
-extern int __libc_missing_posix_timers attribute_hidden;
-
-/* The REALTIME and MONOTONIC clock might be available. Try the
- syscall first. */
-# define SYSDEP_GETRES \
- case CLOCK_REALTIME: \
- case CLOCK_MONOTONIC: \
- { \
- int e = EINVAL; \
- \
- if (!__libc_missing_posix_timers) \
- { \
- INTERNAL_SYSCALL_DECL (err); \
- int r = INTERNAL_SYSCALL (clock_getres, err, 2, clock_id, res); \
- if (!INTERNAL_SYSCALL_ERROR_P (r, err)) \
- { \
- retval = 0; \
- break; \
- } \
- \
- e = INTERNAL_SYSCALL_ERRNO (r, err); \
- if (e == ENOSYS) \
- { \
- __libc_missing_posix_timers = 1; \
- e = EINVAL; \
- } \
- } \
- \
- /* Fallback code. */ \
- if (e == EINVAL && clock_id == CLOCK_REALTIME) \
- HANDLE_REALTIME; \
- else \
- __set_errno (e); \
- } \
- break
+/* This means all clocks are definitely supported in the kernel. */
+int
+clock_getres (clockid_t clock_id, struct timespec *res)
+{
+ INTERNAL_SYSCALL_DECL (err);
+ int r = INTERNAL_SYSCALL (clock_getres, err, 2, clock_id, res);
+ if (!INTERNAL_SYSCALL_ERROR_P (r, err))
+ return 0;
+ return -1;
+}
+
+#else
+
+/*
+ * Need to deal with multiple complex fallback and legacy scenarios
+ *
+ * Give priority to the clock_gettime syscall but fall back if
+ * certain clocks are not available
+ * for CLOCK_REALTIME fall back to gettimeofday
+ * for CLOCK_PROCESS_CPUTIME_ID fall back to HP_TIMING
+ * for CLOCK_THREAD_CPUTIME_ID fall back to pthreads
+ */
+
+int __libc_missing_posix_stdtimers attribute_hidden;
+int __libc_missing_posix_cputimers attribute_hidden;
+
+int
+clock_getres (clockid_t clock_id, struct timespec *res)
+{
+ int retval = -1;
+
+ switch (clock_id)
+ {
+#if defined __NR_clock_gettime
+ case CLOCK_REALTIME: case CLOCK_MONOTONIC:
+ {
+ int e = EINVAL;
+
+ if (!__libc_missing_posix_stdtimers)
+ {
+ INTERNAL_SYSCALL_DECL (err);
+ int r = INTERNAL_SYSCALL (clock_getres, err, 2, clock_id, res);
+ if (!INTERNAL_SYSCALL_ERROR_P (r, err))
+ {
+ retval = 0;
+ break;
+ }
+
+ e = INTERNAL_SYSCALL_ERRNO (r, err);
+ if (e == ENOSYS)
+ {
+ __libc_missing_posix_stdtimers = 1;
+ e = EINVAL;
+ }
+ }
+
+ /* Fallback code. */
+ if (e != EINVAL || clock_id != CLOCK_REALTIME)
+ {
+ __set_errno (e);
+ break;
+ }
+ }
+ /* Fall through */
+#else
+ case CLOCK_REALTIME:
#endif
+ {
+ long int clk_tck = sysconf (_SC_CLK_TCK);
+
+ if (__builtin_expect (clk_tck != -1, 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;
+
+ retval = 0;
+ }
+ }
+ break;
+
+ case CLOCK_PROCESS_CPUTIME_ID: case CLOCK_THREAD_CPUTIME_ID:
+#if defined __NR_clock_gettime
+ if (!__libc_missing_posix_cputimers)
+ {
+ INTERNAL_SYSCALL_DECL (err);
+ int r = INTERNAL_SYSCALL (clock_getres, err, 2, clock_id, res);
+ if (!INTERNAL_SYSCALL_ERROR_P (r, err))
+ {
+ retval = 0;
+ break;
+ }
+
+ if (INTERNAL_SYSCALL_ERRNO (r, err) == ENOSYS)
+ __libc_missing_posix_cputimers = 1;
+ }
+
+#if HP_TIMING_AVAIL
+ /* Fallback code. */
+ static long int nsec;
+ {
+ if (__builtin_expect (nsec == 0, 0))
+ {
+ hp_timing_t freq;
+
+ /* This can only happen if we haven't initialized the `freq'
+ 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 (__builtin_expect (freq == 0, 0))
+ /* Something went wrong. */
+ break;
+ nsec = MAX (UINT64_C (1000000000) / freq, 1);
+ }
+ /* File in the values. The seconds are always zero (unless we
+ have a 1Hz machine). */
+ res->tv_sec = 0;
+ res->tv_nsec = nsec;
+
+ retval = 0;
+ }
+#endif
+#endif
+ break;
+
+ default:
+#if defined __NR_clock_gettime
+ INTERNAL_SYSCALL_DECL (err);
+ int r = INTERNAL_SYSCALL (clock_getres, err, 2, clock_id, res);
+ if (!INTERNAL_SYSCALL_ERROR_P (r, err))
+ retval = 0;
+#else
+ __set_errno(EINVAL);
+#endif
+ }
+ return retval;
+}

-#ifdef __NR_clock_getres
-/* We handled the REALTIME clock here. */
-# define HANDLED_REALTIME 1
#endif

-#include <sysdeps/posix/clock_getres.c>
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/