]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libc/sys/__vdso_gettimeofday.c
Merge ACPICA 20170929.
[FreeBSD/FreeBSD.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 int
38 tc_delta(const struct vdso_timehands *th, u_int *delta)
39 {
40         int error;
41         u_int tc;
42
43         error = __vdso_gettc(th, &tc);
44         if (error == 0)
45                 *delta = (tc - th->th_offset_count) & th->th_counter_mask;
46         return (error);
47 }
48
49 /*
50  * Calculate the absolute or boot-relative time from the
51  * machine-specific fast timecounter and the published timehands
52  * structure read from the shared page.
53  *
54  * The lockless reading scheme is similar to the one used to read the
55  * in-kernel timehands, see sys/kern/kern_tc.c:binuptime().  This code
56  * is based on the kernel implementation.
57  */
58 static int
59 binuptime(struct bintime *bt, struct vdso_timekeep *tk, int abs)
60 {
61         struct vdso_timehands *th;
62         uint32_t curr, gen;
63         u_int delta;
64         int error;
65
66         do {
67                 if (!tk->tk_enabled)
68                         return (ENOSYS);
69
70                 curr = atomic_load_acq_32(&tk->tk_current);
71                 th = &tk->tk_th[curr];
72                 gen = atomic_load_acq_32(&th->th_gen);
73                 *bt = th->th_offset;
74                 error = tc_delta(th, &delta);
75                 if (error == EAGAIN)
76                         continue;
77                 if (error != 0)
78                         return (error);
79                 bintime_addx(bt, th->th_scale * delta);
80                 if (abs)
81                         bintime_add(bt, &th->th_boottime);
82
83                 /*
84                  * Ensure that the load of th_offset is completed
85                  * before the load of th_gen.
86                  */
87                 atomic_thread_fence_acq();
88         } while (curr != tk->tk_current || gen == 0 || gen != th->th_gen);
89         return (0);
90 }
91
92 static struct vdso_timekeep *tk;
93
94 #pragma weak __vdso_gettimeofday
95 int
96 __vdso_gettimeofday(struct timeval *tv, struct timezone *tz)
97 {
98         struct bintime bt;
99         int error;
100
101         if (tz != NULL)
102                 return (ENOSYS);
103         if (tk == NULL) {
104                 error = __vdso_gettimekeep(&tk);
105                 if (error != 0 || tk == NULL)
106                         return (ENOSYS);
107         }
108         if (tk->tk_ver != VDSO_TK_VER_CURR)
109                 return (ENOSYS);
110         error = binuptime(&bt, tk, 1);
111         if (error != 0)
112                 return (error);
113         bintime2timeval(&bt, tv);
114         return (0);
115 }
116
117 #pragma weak __vdso_clock_gettime
118 int
119 __vdso_clock_gettime(clockid_t clock_id, struct timespec *ts)
120 {
121         struct bintime bt;
122         int abs, error;
123
124         if (tk == NULL) {
125                 error = _elf_aux_info(AT_TIMEKEEP, &tk, sizeof(tk));
126                 if (error != 0 || tk == NULL)
127                         return (ENOSYS);
128         }
129         if (tk->tk_ver != VDSO_TK_VER_CURR)
130                 return (ENOSYS);
131         switch (clock_id) {
132         case CLOCK_REALTIME:
133         case CLOCK_REALTIME_PRECISE:
134         case CLOCK_REALTIME_FAST:
135         case CLOCK_SECOND:
136                 abs = 1;
137                 break;
138         case CLOCK_MONOTONIC:
139         case CLOCK_MONOTONIC_PRECISE:
140         case CLOCK_MONOTONIC_FAST:
141         case CLOCK_UPTIME:
142         case CLOCK_UPTIME_PRECISE:
143         case CLOCK_UPTIME_FAST:
144                 abs = 0;
145                 break;
146         default:
147                 return (ENOSYS);
148         }
149         error = binuptime(&bt, tk, abs);
150         if (error != 0)
151                 return (error);
152         bintime2timespec(&bt, ts);
153         if (clock_id == CLOCK_SECOND)
154                 ts->tv_nsec = 0;
155         return (0);
156 }