diff options
author | Ulrich Drepper <drepper@redhat.com> | 2001-04-21 07:55:01 +0000 |
---|---|---|
committer | Ulrich Drepper <drepper@redhat.com> | 2001-04-21 07:55:01 +0000 |
commit | 5fc48cd78f0c50e889d1c1f3b8f812058db71ecd (patch) | |
tree | 97c81fc8d9405b4f9228f2a76282017e887d8b13 | |
parent | b17c0a8e31fe9e99359b6a874008de7aba85d64e (diff) | |
download | glibc-5fc48cd78f0c50e889d1c1f3b8f812058db71ecd.tar.gz glibc-5fc48cd78f0c50e889d1c1f3b8f812058db71ecd.tar.xz glibc-5fc48cd78f0c50e889d1c1f3b8f812058db71ecd.zip |
Update.
2001-04-21 Ulrich Drepper <drepper@redhat.com> * elf/dl-support.c: Include cpuclock-init.h. Use CPUCLOCK_VARDEF and CPUCLOCK_INIT if defined. * sysdeps/generic/dl-sysdep.c: Likewise. * sysdeps/generic/cpuclock-init.h: New file. * sysdeps/unix/i386/i586/cpuclock-init.h: New file. * sysdeps/unix/i386/i586/Versions: New file. * sysdeps/unix/i386/i586/clock_settime.c: New file. * sysdeps/unix/i386/i586/clock_gettime.c: Handle thread CPU clock separately by calling __pthread_clock_gettime if this function is available. Subtract offset from tsc value before computing time value.
-rw-r--r-- | ChangeLog | 13 | ||||
-rw-r--r-- | elf/dl-support.c | 10 | ||||
-rw-r--r-- | linuxthreads/ChangeLog | 18 | ||||
-rw-r--r-- | linuxthreads/internals.h | 4 | ||||
-rw-r--r-- | linuxthreads/manager.c | 7 | ||||
-rw-r--r-- | linuxthreads/pthread.c | 8 | ||||
-rw-r--r-- | linuxthreads/sysdeps/i386/i586/Makefile | 3 | ||||
-rw-r--r-- | linuxthreads/sysdeps/i386/i586/Versions | 5 | ||||
-rw-r--r-- | linuxthreads/sysdeps/i386/i586/ptclock_gettime.c | 45 | ||||
-rw-r--r-- | linuxthreads/sysdeps/i386/i586/ptclock_settime.c | 32 | ||||
-rw-r--r-- | linuxthreads/sysdeps/i386/i686/Implies | 1 | ||||
-rw-r--r-- | linuxthreads/sysdeps/i386/useldt.h | 66 | ||||
-rw-r--r-- | sysdeps/generic/cpuclock-init.h | 1 | ||||
-rw-r--r-- | sysdeps/generic/dl-sysdep.c | 9 | ||||
-rw-r--r-- | sysdeps/unix/i386/i586/Versions | 5 | ||||
-rw-r--r-- | sysdeps/unix/i386/i586/clock_gettime.c | 47 | ||||
-rw-r--r-- | sysdeps/unix/i386/i586/clock_settime.c | 85 | ||||
-rw-r--r-- | sysdeps/unix/i386/i586/cpuclock-init.h | 27 |
18 files changed, 358 insertions, 28 deletions
diff --git a/ChangeLog b/ChangeLog index 0dde120a30..d5f5c5f419 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,16 @@ +2001-04-21 Ulrich Drepper <drepper@redhat.com> + + * elf/dl-support.c: Include cpuclock-init.h. Use CPUCLOCK_VARDEF and + CPUCLOCK_INIT if defined. + * sysdeps/generic/dl-sysdep.c: Likewise. + * sysdeps/generic/cpuclock-init.h: New file. + * sysdeps/unix/i386/i586/cpuclock-init.h: New file. + * sysdeps/unix/i386/i586/Versions: New file. + * sysdeps/unix/i386/i586/clock_settime.c: New file. + * sysdeps/unix/i386/i586/clock_gettime.c: Handle thread CPU clock + separately by calling __pthread_clock_gettime if this function is + available. Subtract offset from tsc value before computing time value. + 2001-04-20 Ulrich Drepper <drepper@redhat.com> * iconv/iconv_prog.c (print_known_names): If printing goes not to diff --git a/elf/dl-support.c b/elf/dl-support.c index 8c10180e26..fa1b1874e7 100644 --- a/elf/dl-support.c +++ b/elf/dl-support.c @@ -29,6 +29,7 @@ #include <bits/libc-lock.h> #include <dl-librecon.h> #include <unsecvars.h> +#include <cpuclock-init.h> extern char *__progname; char **_dl_argv = &__progname; /* This is checked for some error messages. */ @@ -92,6 +93,11 @@ struct r_scope_elem *_dl_main_searchlist = &_dl_initial_searchlist; /* Nonzero during startup. */ int _dl_starting_up = 1; +/* Initial value of the CPU clock. */ +#ifdef CPUCLOCK_VARDEF +CPUCLOCK_VARDEF (_dl_cpuclock_offset); +#endif + /* During the program run we must not modify the global data of loaded shared object simultanously in two threads. Therefore we protect `_dl_open' and `_dl_close' in dl-close.c. @@ -127,6 +133,10 @@ static void non_dynamic_init (void) __attribute__ ((unused)); static void non_dynamic_init (void) { +#ifdef CPUCLOCK_INIT + CPUCLOCK_INIT (_dl_cpuclock_offset); +#endif + if (!_dl_pagesize) _dl_pagesize = __getpagesize (); diff --git a/linuxthreads/ChangeLog b/linuxthreads/ChangeLog index ca64e526fd..e74f0bfbe8 100644 --- a/linuxthreads/ChangeLog +++ b/linuxthreads/ChangeLog @@ -1,3 +1,21 @@ +2001-04-21 Ulrich Drepper <drepper@redhat.com> + + * internals.h: Include <cpuclock-init.h>. + (struct _pthread_descr_struct): Add p_cpuclock_offset field if + CPUCLOCK_VARDEF is defined. + * pthread.c (__pthread_initialize_minimal): Initialize + p_cpuclock_offset field for main thread if CPUCLOCK_INIT is defined. + * manager.c (pthread_start_thread): Set p_cpuclock_offset field + for new thread to current CPU clock value. + + * sysdeps/i386/useldt.h: Extend all the macros to handle 8-byte values. + + * sysdeps/i386/i586/Makefile: New file. + * sysdeps/i386/i586/Versions: New file. + * sysdeps/i386/i586/ptclock_gettime.c: New file. + * sysdeps/i386/i586/ptclock_settime.c: New file. + * sysdeps/i386/i686/Implies: New file. + 2001-04-18 Jakub Jelinek <jakub@redhat.com> * sysdeps/unix/sysv/linux/sparc/sparc64/Makefile: Put specs into diff --git a/linuxthreads/internals.h b/linuxthreads/internals.h index 998f7788e8..fbe0ea35d2 100644 --- a/linuxthreads/internals.h +++ b/linuxthreads/internals.h @@ -33,6 +33,7 @@ extern int __compare_and_swap (long int *p, long int oldval, long int newval); #include "pt-machine.h" #include "semaphore.h" #include "../linuxthreads_db/thread_dbP.h" +#include <cpuclock-init.h> #ifndef THREAD_GETMEM # define THREAD_GETMEM(descr, member) descr->member @@ -179,6 +180,9 @@ struct _pthread_descr_struct { struct __res_state *p_resp; /* Pointer to resolver state */ struct __res_state p_res; /* per-thread resolver state */ int p_inheritsched; /* copied from the thread attribute */ +#ifdef CPUCLOCK_VARDEF + CPUCLOCK_VARDEF (p_cpuclock_offset); /* Initial CPU clock for thread. */ +#endif /* New elements must be added at the end. */ } __attribute__ ((aligned(32))); /* We need to align the structure so that doubles are aligned properly. This is 8 diff --git a/linuxthreads/manager.c b/linuxthreads/manager.c index 431e149212..dbe2aa2d19 100644 --- a/linuxthreads/manager.c +++ b/linuxthreads/manager.c @@ -225,10 +225,17 @@ static int pthread_start_thread(void *arg) pthread_descr self = (pthread_descr) arg; struct pthread_request request; void * outcome; +#ifdef CPUCLOCK_VARDEF + CPUCLOCK_VARDEF (tmpclock); +#endif /* Initialize special thread_self processing, if any. */ #ifdef INIT_THREAD_SELF INIT_THREAD_SELF(self, self->p_nr); #endif +#ifdef CPUCLOCK_INIT + CPUCLOCK_INIT (tmpclock); + THREAD_SETMEM (self, p_cpuclock_offset, tmpclock); +#endif /* Make sure our pid field is initialized, just in case we get there before our father has initialized it. */ THREAD_SETMEM(self, p_pid, __getpid()); diff --git a/linuxthreads/pthread.c b/linuxthreads/pthread.c index ff9c083010..73a7acb9c9 100644 --- a/linuxthreads/pthread.c +++ b/linuxthreads/pthread.c @@ -225,6 +225,11 @@ static void pthread_handle_sigcancel(int sig); static void pthread_handle_sigrestart(int sig); static void pthread_handle_sigdebug(int sig); +/* CPU clock handling. */ +#ifdef CPUCLOCK_VARDECL +CPUCLOCK_VARDECL (_dl_cpuclock_offset); +#endif + /* Signal numbers used for the communication. In these variables we keep track of the used variables. If the platform does not support any real-time signals we will define the @@ -390,6 +395,9 @@ __pthread_initialize_minimal(void) #ifdef INIT_THREAD_SELF INIT_THREAD_SELF(&__pthread_initial_thread, 0); #endif +#ifdef CPUCLOCK_INIT + __pthread_initial_thread.p_cpuclock_offset = _dl_cpuclock_offset; +#endif } diff --git a/linuxthreads/sysdeps/i386/i586/Makefile b/linuxthreads/sysdeps/i386/i586/Makefile new file mode 100644 index 0000000000..96c195f6e5 --- /dev/null +++ b/linuxthreads/sysdeps/i386/i586/Makefile @@ -0,0 +1,3 @@ +ifeq ($(subdir),linuxthreads) +libpthread-sysdep_routines += ptclock_gettime ptclock_settime +endif diff --git a/linuxthreads/sysdeps/i386/i586/Versions b/linuxthreads/sysdeps/i386/i586/Versions new file mode 100644 index 0000000000..f9921aff55 --- /dev/null +++ b/linuxthreads/sysdeps/i386/i586/Versions @@ -0,0 +1,5 @@ +libpthread { + GLIBC_2.2.3 { + __pthread_clock_gettime; __pthread_clock_settime; + } +} diff --git a/linuxthreads/sysdeps/i386/i586/ptclock_gettime.c b/linuxthreads/sysdeps/i386/i586/ptclock_gettime.c new file mode 100644 index 0000000000..818251074e --- /dev/null +++ b/linuxthreads/sysdeps/i386/i586/ptclock_gettime.c @@ -0,0 +1,45 @@ +/* Copyright (C) 2001 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 Library General Public License as + published by the Free Software Foundation; either version 2 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#include <time.h> +#include <libc-internal.h> + +#include "../../../internals.h" + + +int +__pthread_clock_gettime (unsigned long long int freq, struct timespec *tp) +{ + unsigned long long int tsc; + pthread_descr self = thread_self (); + + /* Get the current counter. */ + asm volatile ("rdtsc" : "=A" (tsc)); + + /* Compute the offset since the start time of the process. */ + tsc -= THREAD_GETMEM (self, p_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) * 1000000000ull) / freq; + + return 0; +} diff --git a/linuxthreads/sysdeps/i386/i586/ptclock_settime.c b/linuxthreads/sysdeps/i386/i586/ptclock_settime.c new file mode 100644 index 0000000000..3026528cc2 --- /dev/null +++ b/linuxthreads/sysdeps/i386/i586/ptclock_settime.c @@ -0,0 +1,32 @@ +/* Copyright (C) 2001 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 Library General Public License as + published by the Free Software Foundation; either version 2 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#include <time.h> +#include <libc-internal.h> + +#include "../../../internals.h" + + +void +__pthread_clock_settime (unsigned long long int offset) +{ + pthread_descr self = thread_self (); + + /* Compute the offset since the start time of the process. */ + THREAD_SETMEM (self, p_cpuclock_offset, offset); +} diff --git a/linuxthreads/sysdeps/i386/i686/Implies b/linuxthreads/sysdeps/i386/i686/Implies new file mode 100644 index 0000000000..0df509e35f --- /dev/null +++ b/linuxthreads/sysdeps/i386/i686/Implies @@ -0,0 +1 @@ +i386/i586 diff --git a/linuxthreads/sysdeps/i386/useldt.h b/linuxthreads/sysdeps/i386/useldt.h index ebf520cc39..b9566ace1f 100644 --- a/linuxthreads/sysdeps/i386/useldt.h +++ b/linuxthreads/sysdeps/i386/useldt.h @@ -1,6 +1,6 @@ /* Special definitions for ix86 machine using segment register based thread descriptor. - Copyright (C) 1998, 2000 Free Software Foundation, Inc. + Copyright (C) 1998, 2000, 2001 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by Ulrich Drepper <drepper@cygnus.com>. @@ -88,16 +88,24 @@ extern int __modify_ldt (int, struct modify_ldt_ldt_s *, size_t); : "0" (0), \ "i" (offsetof (struct _pthread_descr_struct, \ member))); \ + else if (sizeof (__value) == 4) \ + __asm__ __volatile__ ("movl %%gs:%P1,%0" \ + : "=r" (__value) \ + : "i" (offsetof (struct _pthread_descr_struct, \ + member))); \ else \ { \ - if (sizeof (__value) != 4) \ - /* There should not be any value with a size other than 1 or 4. */ \ + if (sizeof (__value) != 8) \ + /* There should not be any value with a size other than 1, 4 or 8. */\ abort (); \ \ - __asm__ __volatile__ ("movl %%gs:%P1,%0" \ - : "=r" (__value) \ + __asm__ __volatile__ ("movl %%gs:%P1,%%eax\n\t" \ + "movl %%gs:%P2,%%edx" \ + : "=A" (__value) \ : "i" (offsetof (struct _pthread_descr_struct, \ - member))); \ + member)), \ + "i" (offsetof (struct _pthread_descr_struct, \ + member) + 4)); \ } \ __value; \ }) @@ -112,14 +120,20 @@ extern int __modify_ldt (int, struct modify_ldt_ldt_s *, size_t); : "0" (0), \ "r" (offsetof (struct _pthread_descr_struct, \ member))); \ + else if (sizeof (__value) == 4) \ + __asm__ __volatile__ ("movl %%gs:(%1),%0" \ + : "=r" (__value) \ + : "r" (offsetof (struct _pthread_descr_struct, \ + member))); \ else \ { \ - if (sizeof (__value) != 4) \ - /* There should not be any value with a size other than 1 or 4. */ \ + if (sizeof (__value) != 8) \ + /* There should not be any value with a size other than 1, 4 or 8. */\ abort (); \ \ - __asm__ __volatile__ ("movl %%gs:(%1),%0" \ - : "=r" (__value) \ + __asm__ __volatile__ ("movl %%gs:(%1),%%eax\n\t" \ + "movl %%gs:4(%1),%%edx" \ + : "=&A" (__value) \ : "r" (offsetof (struct _pthread_descr_struct, \ member))); \ } \ @@ -135,16 +149,24 @@ extern int __modify_ldt (int, struct modify_ldt_ldt_s *, size_t); : "q" (__value), \ "i" (offsetof (struct _pthread_descr_struct, \ member))); \ + else if (sizeof (__value) == 4) \ + __asm__ __volatile__ ("movl %0,%%gs:%P1" : \ + : "r" (__value), \ + "i" (offsetof (struct _pthread_descr_struct, \ + member))); \ else \ { \ - if (sizeof (__value) != 4) \ - /* There should not be any value with a size other than 1 or 4. */ \ + if (sizeof (__value) != 8) \ + /* There should not be any value with a size other than 1, 4 or 8. */\ abort (); \ \ - __asm__ __volatile__ ("movl %0,%%gs:%P1" : \ - : "r" (__value), \ + __asm__ __volatile__ ("movl %%eax,%%gs:%P1\n\n" \ + "movl %%edx,%%gs:%P2" : \ + : "A" (__value), \ "i" (offsetof (struct _pthread_descr_struct, \ - member))); \ + member)), \ + "i" (offsetof (struct _pthread_descr_struct, \ + member) + 4)); \ } \ }) @@ -157,14 +179,20 @@ extern int __modify_ldt (int, struct modify_ldt_ldt_s *, size_t); : "q" (__value), \ "r" (offsetof (struct _pthread_descr_struct, \ member))); \ + else if (sizeof (__value) == 4) \ + __asm__ __volatile__ ("movl %0,%%gs:(%1)" : \ + : "r" (__value), \ + "r" (offsetof (struct _pthread_descr_struct, \ + member))); \ else \ { \ - if (sizeof (__value) != 4) \ - /* There should not be any value with a size other than 1 or 4. */ \ + if (sizeof (__value) != 8) \ + /* There should not be any value with a size other than 1, 4 or 8. */\ abort (); \ \ - __asm__ __volatile__ ("movl %0,%%gs:(%1)" : \ - : "r" (__value), \ + __asm__ __volatile__ ("movl %%eax,%%gs:(%1)\n\t" \ + "movl %%edx,%%gs:4(%1)" : \ + : "A" (__value), \ "r" (offsetof (struct _pthread_descr_struct, \ member))); \ } \ diff --git a/sysdeps/generic/cpuclock-init.h b/sysdeps/generic/cpuclock-init.h new file mode 100644 index 0000000000..085cf179da --- /dev/null +++ b/sysdeps/generic/cpuclock-init.h @@ -0,0 +1 @@ +/* In general there is no CPU clock. */ diff --git a/sysdeps/generic/dl-sysdep.c b/sysdeps/generic/dl-sysdep.c index eb70d3de80..024864f72f 100644 --- a/sysdeps/generic/dl-sysdep.c +++ b/sysdeps/generic/dl-sysdep.c @@ -35,6 +35,7 @@ #include <dl-machine.h> #include <dl-procinfo.h> #include <dl-osinfo.h> +#include <cpuclock-init.h> extern int _dl_argc; extern char **_dl_argv; @@ -60,7 +61,9 @@ int __libc_multiple_libcs = 0; /* Defining this here avoids the inclusion void *__libc_stack_end; static ElfW(auxv_t) *_dl_auxv; unsigned long int _dl_hwcap_mask = HWCAP_IMPORTANT; - +#ifdef CPUCLOCK_VARDEF +CPUCLOCK_VARDEF (_dl_cpuclock_offset); +#endif #ifndef DL_FIND_ARG_COMPONENTS # define DL_FIND_ARG_COMPONENTS(cookie, argc, argv, envp, auxp) \ @@ -97,6 +100,10 @@ _dl_sysdep_start (void **start_argptr, # define set_seen(tag) seen |= M ((tag)->a_type) #endif +#ifdef CPUCLOCK_INIT + CPUCLOCK_INIT (_dl_cpuclock_offset); +#endif + DL_FIND_ARG_COMPONENTS (start_argptr, _dl_argc, _dl_argv, _environ, _dl_auxv); diff --git a/sysdeps/unix/i386/i586/Versions b/sysdeps/unix/i386/i586/Versions new file mode 100644 index 0000000000..4242ca448a --- /dev/null +++ b/sysdeps/unix/i386/i586/Versions @@ -0,0 +1,5 @@ +ld { + GLIBC_2.2.3 { + _dl_cpuclock_offset; + } +} diff --git a/sysdeps/unix/i386/i586/clock_gettime.c b/sysdeps/unix/i386/i586/clock_gettime.c index 85dcdcfe68..5e28370acf 100644 --- a/sysdeps/unix/i386/i586/clock_gettime.c +++ b/sysdeps/unix/i386/i586/clock_gettime.c @@ -19,6 +19,9 @@ #include <time.h> #include <libc-internal.h> +#include "cpuclock-init.h" + + /* This implementation uses the TSC register in modern (i586 and up) IA-32 processors (most modern clones also provide it). Since we need the resolution of the clock and since determining this is not cheap, we @@ -33,17 +36,42 @@ static unsigned long long int freq; -/* We add an limitation here: we assume that the machine is not up as - long as it takes to wrap-around the 64-bit timestamp counter. On a - 4GHz machine it would take 136 years of uptime to wrap around so - this "limitation" is not severe. +/* We need the starting time for the process. */ +CPUCLOCK_VARDECL (_dl_cpuclock_offset); + + +/* This function is defined in the thread library. */ +extern int __pthread_clock_gettime (unsigned long long int freq, + struct timespec *tp) + __attribute__ ((__weak__)); + - We use this clock also as the monotonic clock since we don't allow - setting the CPU-time clock. If this should ever change we will have - to separate the two. */ +/* We add an limitation here: we assume that the process is not + running as long as it takes to wrap-around the 64-bit timestamp + counter. On a 4GHz machine it would take 136 years of uptime to + wrap around so this "limitation" is not severe. */ #define EXTRA_CLOCK_CASES \ - case CLOCK_PROCESS_CPUTIME_ID: \ case CLOCK_THREAD_CPUTIME_ID: \ + if (__pthread_clock_gettime != NULL) \ + { \ + 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; \ + } \ + \ + retval = __pthread_clock_gettime (freq, tp); \ + break; \ + } \ + /* FALLTHROUGH */ \ + \ + case CLOCK_PROCESS_CPUTIME_ID: \ { \ unsigned long long int tsc; \ \ @@ -62,6 +90,9 @@ static unsigned long long int freq; /* Get the current counter. */ \ asm volatile ("rdtsc" : "=A" (tsc)); \ \ + /* Compute the offset since the start time of the process. */ \ + tsc -= _dl_cpuclock_offset; \ + \ /* Compute the seconds. */ \ tp->tv_sec = tsc / freq; \ \ diff --git a/sysdeps/unix/i386/i586/clock_settime.c b/sysdeps/unix/i386/i586/clock_settime.c new file mode 100644 index 0000000000..465a68f694 --- /dev/null +++ b/sysdeps/unix/i386/i586/clock_settime.c @@ -0,0 +1,85 @@ +/* Copyright (C) 2001 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 Library General Public License as + published by the Free Software Foundation; either version 2 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#include <time.h> + +#include "cpuclock-init.h" + + +/* This implementation uses the TSC register in modern (i586 and up) IA-32 + processors (most modern clones also provide it). Since we need the + resolution of the clock and since determining this is not cheap, we + cache the value. But this means that systems with processors running + at different speeds or process migration to machines with slower or + faster processors will not work without changes. */ + + +/* 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 unsigned long long int freq; + + +/* We need the starting time for the process. */ +CPUCLOCK_VARDECL (_dl_cpuclock_offset); + + +/* This function is defined in the thread library. */ +extern void __pthread_clock_settime (unsigned long long int offset) + __attribute__ ((__weak__)); + + +/* We add an limitation here: we assume that the process is not + running as long as it takes to wrap-around the 64-bit timestamp + counter. On a 4GHz machine it would take 136 years of uptime to + wrap around so this "limitation" is not severe. */ +#define EXTRA_CLOCK_CASES \ + case CLOCK_PROCESS_CPUTIME_ID: \ + case CLOCK_THREAD_CPUTIME_ID: \ + { \ + unsigned long long int tsc; \ + unsigned long long int usertime; \ + \ + /* First thing is to get the current time. */ \ + asm volatile ("rdtsc" : "=A" (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; \ + } \ + \ + /* 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_THREAD_CPUTIME_ID \ + || __pthread_clock_settime == NULL) \ + _dl_cpuclock_offset = tsc - usertime; \ + else \ + __pthread_clock_settime (tsc - usertime); \ + } \ + break; + +#include <sysdeps/unix/clock_settime.c> diff --git a/sysdeps/unix/i386/i586/cpuclock-init.h b/sysdeps/unix/i386/i586/cpuclock-init.h new file mode 100644 index 0000000000..ff1ff9752a --- /dev/null +++ b/sysdeps/unix/i386/i586/cpuclock-init.h @@ -0,0 +1,27 @@ +/* x86 TSC based CPU clock initialization code. + Copyright (C) 2001 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 Library General Public License as + published by the Free Software Foundation; either version 2 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +/* The TSC value has 64 bits. */ +#define CPUCLOCK_VARDEF(name) unsigned long long int name + +/* A declaration. */ +#define CPUCLOCK_VARDECL(name) extern unsigned long long int name + +/* Reading the TSC value is a simple instruction. */ +#define CPUCLOCK_INIT(name) asm volatile ("rdtsc" : "=A" (name)) |