2 * SPDX-License-Identifier: BSD-2-Clause
4 * Copyright (c) 2012 Konstantin Belousov <kib@FreeBSD.org>
5 * Copyright (c) 2021 Dmitry Chagin <dchagin@FreeBSD.org>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 return ((__builtin_clz(mask) ^ 0x1f) + 1);
46 for (bit = 1; mask != 1; bit++)
47 mask = (unsigned long)mask >> 1;
58 for (bit = 1; mask != 1; bit++)
59 mask = (unsigned long long)mask >> 1;
65 __vdso_native_to_linux_timespec(struct l_timespec *lts,
70 if (nts->tv_sec > INT_MAX || nts->tv_sec < INT_MIN)
71 return (LINUX_EOVERFLOW);
73 lts->tv_sec = nts->tv_sec;
74 lts->tv_nsec = nts->tv_nsec;
79 __vdso_native_to_linux_timeval(l_timeval *ltv,
84 if (ntv->tv_sec > INT_MAX || ntv->tv_sec < INT_MIN)
85 return (LINUX_EOVERFLOW);
87 ltv->tv_sec = ntv->tv_sec;
88 ltv->tv_usec = ntv->tv_usec;
93 #if defined(__i386__) || (defined(__amd64__) && defined(COMPAT_LINUX32))
95 __vdso_native_to_linux_timespec64(struct l_timespec64 *lts,
99 lts->tv_sec = nts->tv_sec;
100 lts->tv_nsec = nts->tv_nsec;
106 __vdso_linux_to_native_clockid(clockid_t *n, clockid_t l)
110 case LINUX_CLOCK_REALTIME:
113 case LINUX_CLOCK_MONOTONIC:
114 *n = CLOCK_MONOTONIC;
116 case LINUX_CLOCK_REALTIME_COARSE:
117 *n = CLOCK_REALTIME_FAST;
119 case LINUX_CLOCK_MONOTONIC_COARSE:
120 case LINUX_CLOCK_MONOTONIC_RAW:
121 *n = CLOCK_MONOTONIC_FAST;
123 case LINUX_CLOCK_BOOTTIME:
127 return (LINUX_EINVAL);
133 * The code below adapted from
134 * lib/libc/sys/__vdso_gettimeofday.c
138 __vdso_gettimekeep(struct vdso_timekeep **tk)
141 *tk = (struct vdso_timekeep *)kern_timekeep_base;
145 tc_delta(const struct vdso_timehands *th, u_int *delta)
150 error = __vdso_gettc(th, &tc);
152 *delta = (tc - th->th_offset_count) & th->th_counter_mask;
157 * Calculate the absolute or boot-relative time from the
158 * machine-specific fast timecounter and the published timehands
159 * structure read from the shared page.
161 * The lockless reading scheme is similar to the one used to read the
162 * in-kernel timehands, see sys/kern/kern_tc.c:binuptime(). This code
163 * is based on the kernel implementation.
166 freebsd_binuptime(struct bintime *bt, struct vdso_timekeep *tk, bool abs)
168 struct vdso_timehands *th;
171 u_int delta, scale_bits;
178 curr = atomic_load_acq_32(&tk->tk_current);
179 th = &tk->tk_th[curr];
180 gen = atomic_load_acq_32(&th->th_gen);
182 error = tc_delta(th, &delta);
187 scale = th->th_scale;
189 scale_bits = flsl(scale);
191 scale_bits = flsll(scale);
193 if (__predict_false(scale_bits + fls(delta) > 63)) {
194 x = (scale >> 32) * delta;
197 bintime_addx(bt, x << 32);
199 bintime_addx(bt, scale * delta);
201 bintime_add(bt, &th->th_boottime);
204 * Ensure that the load of th_offset is completed
205 * before the load of th_gen.
207 atomic_thread_fence_acq();
208 } while (curr != tk->tk_current || gen == 0 || gen != th->th_gen);
213 freebsd_getnanouptime(struct bintime *bt, struct vdso_timekeep *tk)
215 struct vdso_timehands *th;
222 curr = atomic_load_acq_32(&tk->tk_current);
223 th = &tk->tk_th[curr];
224 gen = atomic_load_acq_32(&th->th_gen);
228 * Ensure that the load of th_offset is completed
229 * before the load of th_gen.
231 atomic_thread_fence_acq();
232 } while (curr != tk->tk_current || gen == 0 || gen != th->th_gen);
237 freebsd_gettimeofday(struct timeval *tv, struct timezone *tz)
239 struct vdso_timekeep *tk;
245 __vdso_gettimekeep(&tk);
248 if (tk->tk_ver != VDSO_TK_VER_CURR)
250 error = freebsd_binuptime(&bt, tk, true);
252 bintime2timeval(&bt, tv);
257 freebsd_clock_gettime(clockid_t clock_id, struct timespec *ts)
259 struct vdso_timekeep *tk;
263 __vdso_gettimekeep(&tk);
266 if (tk->tk_ver != VDSO_TK_VER_CURR)
270 case CLOCK_REALTIME_PRECISE:
271 case CLOCK_REALTIME_FAST:
272 error = freebsd_binuptime(&bt, tk, true);
274 case CLOCK_MONOTONIC:
275 case CLOCK_MONOTONIC_PRECISE:
277 case CLOCK_UPTIME_PRECISE:
278 error = freebsd_binuptime(&bt, tk, false);
280 case CLOCK_MONOTONIC_FAST:
281 case CLOCK_UPTIME_FAST:
282 error = freebsd_getnanouptime(&bt, tk);
289 bintime2timespec(&bt, ts);
294 * Linux vDSO interfaces
298 __vdso_clock_gettime(clockid_t clock_id, struct l_timespec *lts)
304 error = __vdso_linux_to_native_clockid(&which, clock_id);
306 return (__vdso_clock_gettime_fallback(clock_id, lts));
307 error = freebsd_clock_gettime(which, &ts);
309 return (-__vdso_native_to_linux_timespec(lts, &ts));
311 return (__vdso_clock_gettime_fallback(clock_id, lts));
315 __vdso_gettimeofday(l_timeval *ltv, struct timezone *tz)
320 error = freebsd_gettimeofday(&tv, tz);
322 return (__vdso_gettimeofday_fallback(ltv, tz));
323 return (-__vdso_native_to_linux_timeval(ltv, &tv));
327 __vdso_clock_getres(clockid_t clock_id, struct l_timespec *lts)
330 return (__vdso_clock_getres_fallback(clock_id, lts));
333 #if defined(__i386__) || defined(COMPAT_LINUX32)
335 __vdso_clock_gettime64(clockid_t clock_id, struct l_timespec64 *lts)
341 error = __vdso_linux_to_native_clockid(&which, clock_id);
343 return (__vdso_clock_gettime64_fallback(clock_id, lts));
344 error = freebsd_clock_gettime(which, &ts);
346 return(-__vdso_native_to_linux_timespec64(lts, &ts));
348 return(__vdso_clock_gettime64_fallback(clock_id, lts));
351 int clock_gettime64(clockid_t clock_id, struct l_timespec64 *lts)
352 __attribute__((weak, alias("__vdso_clock_gettime64")));
355 #if defined(__i386__) || defined(__amd64__)
357 __vdso_getcpu(uint32_t *cpu, uint32_t *node, void *cache)
362 return (__vdso_getcpu_fallback(cpu, node, cache));
363 ret = __vdso_getcpu_try();
365 return (__vdso_getcpu_fallback(cpu, node, cache));
371 #if defined(__i386__) || defined(__amd64__)
373 __vdso_time(long *tm)
378 error = freebsd_gettimeofday(&tv, NULL);
380 return (__vdso_time_fallback(tm));