2005-12-08 Steven Munroe <sjmunroe@us.ibm.com>
[platform/upstream/glibc.git] / sysdeps / unix / sysv / linux / clock_gettime.c
1 /* clock_gettime -- Get current time from a POSIX clockid_t.  Linux version.
2    Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9
10    The GNU C Library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14
15    You should have received a copy of the GNU Lesser General Public
16    License along with the GNU C Library; if not, write to the Free
17    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
18    02111-1307 USA.  */
19
20 #include <sysdep.h>
21 #include <errno.h>
22 #include <time.h>
23 #include "kernel-posix-cpu-timers.h"
24 #include "kernel-features.h"
25
26 #ifndef HAVE_CLOCK_GETTIME_VSYSCALL
27 # undef INTERNAL_VSYSCALL
28 # define INTERNAL_VSYSCALL INTERNAL_SYSCALL
29 # undef INLINE_VSYSCALL
30 # define INLINE_VSYSCALL INLINE_SYSCALL
31 #else
32 # include <bits/libc-vdso.h>
33 #endif
34
35 #define SYSCALL_GETTIME \
36   retval = INLINE_VSYSCALL (clock_gettime, 2, clock_id, tp); \
37   break
38
39 #ifdef __ASSUME_POSIX_TIMERS
40
41 /* This means the REALTIME and MONOTONIC clock are definitely
42    supported in the kernel.  */
43 # define SYSDEP_GETTIME                                                       \
44   SYSDEP_GETTIME_CPUTIME                                                      \
45   case CLOCK_REALTIME:                                                        \
46   case CLOCK_MONOTONIC:                                                       \
47     SYSCALL_GETTIME
48
49 # define __libc_missing_posix_timers 0
50 #elif defined __NR_clock_gettime
51 /* Is the syscall known to exist?  */
52 int __libc_missing_posix_timers attribute_hidden;
53
54 static inline int
55 maybe_syscall_gettime (clockid_t clock_id, struct timespec *tp)
56 {
57   int e = EINVAL;
58
59   if (!__libc_missing_posix_timers)
60     {
61       INTERNAL_SYSCALL_DECL (err);
62       int r = INTERNAL_VSYSCALL (clock_gettime, err, 2, clock_id, tp);
63       if (!INTERNAL_SYSCALL_ERROR_P (r, err))
64         return 0;
65
66       e = INTERNAL_SYSCALL_ERRNO (r, err);
67       if (e == ENOSYS)
68         {
69           __libc_missing_posix_timers = 1;
70           e = EINVAL;
71         }
72     }
73
74   return e;
75 }
76
77 /* The REALTIME and MONOTONIC clock might be available.  Try the
78    syscall first.  */
79 # define SYSDEP_GETTIME                                                       \
80   SYSDEP_GETTIME_CPUTIME                                                      \
81   case CLOCK_REALTIME:                                                        \
82   case CLOCK_MONOTONIC:                                                       \
83     retval = maybe_syscall_gettime (clock_id, tp);                            \
84     if (retval == 0)                                                          \
85       break;                                                                  \
86     /* Fallback code.  */                                                     \
87     if (retval == EINVAL && clock_id == CLOCK_REALTIME)                       \
88       retval = realtime_gettime (tp);                                         \
89     else                                                                      \
90       {                                                                       \
91         __set_errno (retval);                                                 \
92         retval = -1;                                                          \
93       }                                                                       \
94     break;
95 #endif
96
97 #ifdef __NR_clock_gettime
98 /* We handled the REALTIME clock here.  */
99 # define HANDLED_REALTIME       1
100 # define HANDLED_CPUTIME        1
101
102 # if __ASSUME_POSIX_CPU_TIMERS > 0
103
104 #  define SYSDEP_GETTIME_CPU SYSCALL_GETTIME
105 #  define SYSDEP_GETTIME_CPUTIME        /* Default catches them too.  */
106
107 # else
108
109 int __libc_missing_posix_cpu_timers attribute_hidden;
110
111 static int
112 maybe_syscall_gettime_cpu (clockid_t clock_id, struct timespec *tp)
113 {
114   int e = EINVAL;
115
116   if (!__libc_missing_posix_cpu_timers)
117     {
118       INTERNAL_SYSCALL_DECL (err);
119       int r = INTERNAL_VSYSCALL (clock_gettime, err, 2, clock_id, tp);
120       if (!INTERNAL_SYSCALL_ERROR_P (r, err))
121         return 0;
122
123       e = INTERNAL_SYSCALL_ERRNO (r, err);
124 # ifndef __ASSUME_POSIX_TIMERS
125       if (e == ENOSYS)
126         {
127           __libc_missing_posix_timers = 1;
128           __libc_missing_posix_cpu_timers = 1;
129           e = EINVAL;
130         }
131       else
132 # endif
133         {
134           if (e == EINVAL)
135             {
136               /* Check whether the kernel supports CPU clocks at all.
137                  If not, record it for the future.  */
138               r = INTERNAL_VSYSCALL (clock_getres, err, 2,
139                                     MAKE_PROCESS_CPUCLOCK (0, CPUCLOCK_SCHED),
140                                     NULL);
141               if (INTERNAL_SYSCALL_ERROR_P (r, err))
142                 __libc_missing_posix_cpu_timers = 1;
143             }
144         }
145     }
146
147   return e;
148 }
149
150 #  define SYSDEP_GETTIME_CPU                                                  \
151   retval = maybe_syscall_gettime_cpu (clock_id, tp);                          \
152   if (retval == 0)                                                            \
153     break;                                                                    \
154   if (retval != EINVAL || !__libc_missing_posix_cpu_timers)                   \
155     {                                                                         \
156       __set_errno (retval);                                                   \
157       retval = -1;                                                            \
158       break;                                                                  \
159     }                                                                         \
160   retval = -1 /* Otherwise continue on to the HP_TIMING version.  */;
161
162 static inline int
163 maybe_syscall_gettime_cputime (clockid_t clock_id, struct timespec *tp)
164 {
165   return maybe_syscall_gettime_cpu
166     (clock_id == CLOCK_THREAD_CPUTIME_ID
167      ? MAKE_THREAD_CPUCLOCK (0, CPUCLOCK_SCHED)
168      : MAKE_PROCESS_CPUCLOCK (0, CPUCLOCK_SCHED),
169      tp);
170 }
171
172 #  define SYSDEP_GETTIME_CPUTIME                                              \
173     case CLOCK_PROCESS_CPUTIME_ID:                                            \
174     case CLOCK_THREAD_CPUTIME_ID:                                             \
175       retval = maybe_syscall_gettime_cputime (clock_id, tp);                  \
176       if (retval == 0)                                                        \
177         break;                                                                \
178       if (retval != EINVAL || !__libc_missing_posix_cpu_timers)               \
179         {                                                                     \
180           __set_errno (retval);                                               \
181           retval = -1;                                                        \
182           break;                                                              \
183         }                                                                     \
184       retval = hp_timing_gettime (clock_id, tp);                              \
185       break;
186 #  if !HP_TIMING_AVAIL
187 #   define hp_timing_gettime(clock_id, tp) (__set_errno (EINVAL), -1)
188 #  endif
189
190 # endif
191 #endif
192
193 #include <sysdeps/unix/clock_gettime.c>