]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - lib/libc/sys/__vdso_gettimeofday.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / lib / libc / sys / __vdso_gettimeofday.c
1 /*-
2  * Copyright (c) 2012 Konstantin Belousov <kib@FreeBSD.org>
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23  * SUCH DAMAGE.
24  */
25
26 #include <sys/cdefs.h>
27 __FBSDID("$FreeBSD$");
28
29 #include <sys/elf.h>
30 #include <sys/time.h>
31 #include <sys/vdso.h>
32 #include <errno.h>
33 #include <time.h>
34 #include <machine/atomic.h>
35 #include "libc_private.h"
36
37 static u_int
38 tc_delta(const struct vdso_timehands *th)
39 {
40
41         return ((__vdso_gettc(th) - th->th_offset_count) &
42             th->th_counter_mask);
43 }
44
45 static int
46 binuptime(struct bintime *bt, struct vdso_timekeep *tk, int abs)
47 {
48         struct vdso_timehands *th;
49         uint32_t curr, gen;
50
51         do {
52                 if (!tk->tk_enabled)
53                         return (ENOSYS);
54
55                 /*
56                  * XXXKIB. The load of tk->tk_current should use
57                  * atomic_load_acq_32 to provide load barrier. But
58                  * since tk points to r/o mapped page, x86
59                  * implementation of atomic_load_acq faults.
60                  */
61                 curr = tk->tk_current;
62                 rmb();
63                 th = &tk->tk_th[curr];
64                 if (th->th_algo != VDSO_TH_ALGO_1)
65                         return (ENOSYS);
66                 gen = th->th_gen;
67                 *bt = th->th_offset;
68                 bintime_addx(bt, th->th_scale * tc_delta(th));
69                 if (abs)
70                         bintime_add(bt, &th->th_boottime);
71
72                 /*
73                  * Barrier for load of both tk->tk_current and th->th_gen.
74                  */
75                 rmb();
76         } while (curr != tk->tk_current || gen == 0 || gen != th->th_gen);
77         return (0);
78 }
79
80 static struct vdso_timekeep *tk;
81
82 #pragma weak __vdso_gettimeofday
83 int
84 __vdso_gettimeofday(struct timeval *tv, struct timezone *tz)
85 {
86         struct bintime bt;
87         int error;
88
89         if (tz != NULL)
90                 return (ENOSYS);
91         if (tk == NULL) {
92                 error = __vdso_gettimekeep(&tk);
93                 if (error != 0 || tk == NULL)
94                         return (ENOSYS);
95         }
96         if (tk->tk_ver != VDSO_TK_VER_CURR)
97                 return (ENOSYS);
98         error = binuptime(&bt, tk, 1);
99         if (error != 0)
100                 return (error);
101         bintime2timeval(&bt, tv);
102         return (0);
103 }
104
105 #pragma weak __vdso_clock_gettime
106 int
107 __vdso_clock_gettime(clockid_t clock_id, struct timespec *ts)
108 {
109         struct bintime bt;
110         int abs, error;
111
112         if (tk == NULL) {
113                 error = _elf_aux_info(AT_TIMEKEEP, &tk, sizeof(tk));
114                 if (error != 0 || tk == NULL)
115                         return (ENOSYS);
116         }
117         if (tk->tk_ver != VDSO_TK_VER_CURR)
118                 return (ENOSYS);
119         switch (clock_id) {
120         case CLOCK_REALTIME:
121         case CLOCK_REALTIME_PRECISE:
122         case CLOCK_REALTIME_FAST:
123         case CLOCK_SECOND:
124                 abs = 1;
125                 break;
126         case CLOCK_MONOTONIC:
127         case CLOCK_MONOTONIC_PRECISE:
128         case CLOCK_MONOTONIC_FAST:
129         case CLOCK_UPTIME:
130         case CLOCK_UPTIME_PRECISE:
131         case CLOCK_UPTIME_FAST:
132                 abs = 0;
133                 break;
134         default:
135                 return (ENOSYS);
136         }
137         error = binuptime(&bt, tk, abs);
138         if (error != 0)
139                 return (error);
140         bintime2timespec(&bt, ts);
141         if (clock_id == CLOCK_SECOND)
142                 ts->tv_nsec = 0;
143         return (0);
144 }