xref: /freebsd/sys/compat/linux/linux_vdso_gtod.inc (revision 5a6a4fb284b1a9c4b4105fd6ab8143356f3ef3f7)
19931033bSDmitry Chagin/*-
29931033bSDmitry Chagin * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
39931033bSDmitry Chagin *
49931033bSDmitry Chagin * Copyright (c) 2012 Konstantin Belousov <kib@FreeBSD.org>
59931033bSDmitry Chagin * Copyright (c) 2021 Dmitry Chagin <dchagin@FreeBSD.org>
69931033bSDmitry Chagin *
79931033bSDmitry Chagin * Redistribution and use in source and binary forms, with or without
89931033bSDmitry Chagin * modification, are permitted provided that the following conditions
99931033bSDmitry Chagin * are met:
109931033bSDmitry Chagin * 1. Redistributions of source code must retain the above copyright
119931033bSDmitry Chagin *    notice, this list of conditions and the following disclaimer.
129931033bSDmitry Chagin * 2. Redistributions in binary form must reproduce the above copyright
139931033bSDmitry Chagin *    notice, this list of conditions and the following disclaimer in the
149931033bSDmitry Chagin *    documentation and/or other materials provided with the distribution.
159931033bSDmitry Chagin *
169931033bSDmitry Chagin * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
179931033bSDmitry Chagin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
189931033bSDmitry Chagin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
199931033bSDmitry Chagin * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
209931033bSDmitry Chagin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
219931033bSDmitry Chagin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
229931033bSDmitry Chagin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
239931033bSDmitry Chagin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
249931033bSDmitry Chagin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
259931033bSDmitry Chagin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
269931033bSDmitry Chagin * SUCH DAMAGE.
279931033bSDmitry Chagin */
289931033bSDmitry Chagin
29f3379401SDmitry Chaginstatic int
30f3379401SDmitry Chaginfls(int mask)
31f3379401SDmitry Chagin{
32f3379401SDmitry Chagin
33f3379401SDmitry Chagin	if (mask == 0)
34f3379401SDmitry Chagin		return (0);
35f3379401SDmitry Chagin	return ((__builtin_clz(mask) ^ 0x1f) + 1);
36f3379401SDmitry Chagin}
37f3379401SDmitry Chagin
38f3379401SDmitry Chagin#ifdef _LP64
39f3379401SDmitry Chaginstatic int
40f3379401SDmitry Chaginffsl(long mask)
41f3379401SDmitry Chagin{
42f3379401SDmitry Chagin	int bit;
43f3379401SDmitry Chagin
44f3379401SDmitry Chagin	if (mask == 0)
45f3379401SDmitry Chagin		return (0);
46f3379401SDmitry Chagin	for (bit = 1; !(mask & 1); bit++)
47f3379401SDmitry Chagin		mask = (unsigned long)mask >> 1;
48f3379401SDmitry Chagin	return (bit);
49f3379401SDmitry Chagin}
50f3379401SDmitry Chagin#else
51f3379401SDmitry Chaginstatic int
52f3379401SDmitry Chaginffsll(long long mask)
53f3379401SDmitry Chagin{
54f3379401SDmitry Chagin	int bit;
55f3379401SDmitry Chagin
56f3379401SDmitry Chagin	if (mask == 0)
57f3379401SDmitry Chagin		return (0);
58f3379401SDmitry Chagin	for (bit = 1; !(mask & 1); bit++)
59f3379401SDmitry Chagin		mask = (unsigned long long)mask >> 1;
60f3379401SDmitry Chagin	return (bit);
61f3379401SDmitry Chagin}
62f3379401SDmitry Chagin#endif
639931033bSDmitry Chagin
649931033bSDmitry Chaginstatic int
659931033bSDmitry Chagin__vdso_native_to_linux_timespec(struct l_timespec *lts,
669931033bSDmitry Chagin    struct timespec *nts)
679931033bSDmitry Chagin{
689931033bSDmitry Chagin
699931033bSDmitry Chagin#ifdef COMPAT_LINUX32
709931033bSDmitry Chagin	if (nts->tv_sec > INT_MAX || nts->tv_sec < INT_MIN)
719931033bSDmitry Chagin		return (LINUX_EOVERFLOW);
729931033bSDmitry Chagin#endif
739931033bSDmitry Chagin	lts->tv_sec = nts->tv_sec;
749931033bSDmitry Chagin	lts->tv_nsec = nts->tv_nsec;
759931033bSDmitry Chagin	return (0);
769931033bSDmitry Chagin}
779931033bSDmitry Chagin
789931033bSDmitry Chaginstatic int
799931033bSDmitry Chagin__vdso_native_to_linux_timeval(l_timeval *ltv,
809931033bSDmitry Chagin    struct timeval *ntv)
819931033bSDmitry Chagin{
829931033bSDmitry Chagin
839931033bSDmitry Chagin#ifdef COMPAT_LINUX32
849931033bSDmitry Chagin	if (ntv->tv_sec > INT_MAX || ntv->tv_sec < INT_MIN)
859931033bSDmitry Chagin		return (LINUX_EOVERFLOW);
869931033bSDmitry Chagin#endif
879931033bSDmitry Chagin	ltv->tv_sec = ntv->tv_sec;
889931033bSDmitry Chagin	ltv->tv_usec = ntv->tv_usec;
899931033bSDmitry Chagin	return (0);
909931033bSDmitry Chagin}
919931033bSDmitry Chagin
929931033bSDmitry Chagin
939931033bSDmitry Chagin#if defined(__i386__) || (defined(__amd64__) && defined(COMPAT_LINUX32))
949931033bSDmitry Chaginstatic int
959931033bSDmitry Chagin__vdso_native_to_linux_timespec64(struct l_timespec64 *lts,
969931033bSDmitry Chagin    struct timespec *nts)
979931033bSDmitry Chagin{
989931033bSDmitry Chagin
999931033bSDmitry Chagin	lts->tv_sec = nts->tv_sec;
1009931033bSDmitry Chagin	lts->tv_nsec = nts->tv_nsec;
1019931033bSDmitry Chagin	return (0);
1029931033bSDmitry Chagin}
1039931033bSDmitry Chagin#endif
1049931033bSDmitry Chagin
1059931033bSDmitry Chaginstatic int
1069931033bSDmitry Chagin__vdso_linux_to_native_clockid(clockid_t *n, clockid_t l)
1079931033bSDmitry Chagin{
1089931033bSDmitry Chagin
1099931033bSDmitry Chagin	switch (l) {
1109931033bSDmitry Chagin	case LINUX_CLOCK_REALTIME:
1119931033bSDmitry Chagin		*n = CLOCK_REALTIME;
1129931033bSDmitry Chagin		break;
1139931033bSDmitry Chagin	case LINUX_CLOCK_MONOTONIC:
1149931033bSDmitry Chagin		*n = CLOCK_MONOTONIC;
1159931033bSDmitry Chagin		break;
1169931033bSDmitry Chagin	case LINUX_CLOCK_REALTIME_COARSE:
1179931033bSDmitry Chagin		*n = CLOCK_REALTIME_FAST;
1189931033bSDmitry Chagin		break;
1199931033bSDmitry Chagin	case LINUX_CLOCK_MONOTONIC_COARSE:
1209931033bSDmitry Chagin	case LINUX_CLOCK_MONOTONIC_RAW:
1219931033bSDmitry Chagin		*n = CLOCK_MONOTONIC_FAST;
1229931033bSDmitry Chagin		break;
1239931033bSDmitry Chagin	case LINUX_CLOCK_BOOTTIME:
1249931033bSDmitry Chagin		*n = CLOCK_UPTIME;
1259931033bSDmitry Chagin		break;
1269931033bSDmitry Chagin	default:
1279931033bSDmitry Chagin		return (LINUX_EINVAL);
1289931033bSDmitry Chagin	}
1299931033bSDmitry Chagin	return (0);
1309931033bSDmitry Chagin}
1319931033bSDmitry Chagin
1329931033bSDmitry Chagin/*
1339931033bSDmitry Chagin * The code below adapted from
1349931033bSDmitry Chagin * lib/libc/sys/__vdso_gettimeofday.c
1359931033bSDmitry Chagin */
1369931033bSDmitry Chagin
1379931033bSDmitry Chaginstatic inline void
1389931033bSDmitry Chagin__vdso_gettimekeep(struct vdso_timekeep **tk)
1399931033bSDmitry Chagin{
1409931033bSDmitry Chagin
1419931033bSDmitry Chagin	*tk = (struct vdso_timekeep *)kern_timekeep_base;
1429931033bSDmitry Chagin}
1439931033bSDmitry Chagin
1449931033bSDmitry Chaginstatic int
1459931033bSDmitry Chagintc_delta(const struct vdso_timehands *th, u_int *delta)
1469931033bSDmitry Chagin{
1479931033bSDmitry Chagin	int error;
1489931033bSDmitry Chagin	u_int tc;
1499931033bSDmitry Chagin
1509931033bSDmitry Chagin	error = __vdso_gettc(th, &tc);
1519931033bSDmitry Chagin	if (error == 0)
1529931033bSDmitry Chagin		*delta = (tc - th->th_offset_count) & th->th_counter_mask;
1539931033bSDmitry Chagin	return (error);
1549931033bSDmitry Chagin}
1559931033bSDmitry Chagin
1569931033bSDmitry Chagin/*
1579931033bSDmitry Chagin * Calculate the absolute or boot-relative time from the
1589931033bSDmitry Chagin * machine-specific fast timecounter and the published timehands
1599931033bSDmitry Chagin * structure read from the shared page.
1609931033bSDmitry Chagin *
1619931033bSDmitry Chagin * The lockless reading scheme is similar to the one used to read the
1629931033bSDmitry Chagin * in-kernel timehands, see sys/kern/kern_tc.c:binuptime().  This code
1639931033bSDmitry Chagin * is based on the kernel implementation.
1649931033bSDmitry Chagin */
1659931033bSDmitry Chaginstatic int
1669931033bSDmitry Chaginfreebsd_binuptime(struct bintime *bt, struct vdso_timekeep *tk, bool abs)
1679931033bSDmitry Chagin{
1689931033bSDmitry Chagin	struct vdso_timehands *th;
1699931033bSDmitry Chagin	uint32_t curr, gen;
1709931033bSDmitry Chagin	uint64_t scale, x;
1719931033bSDmitry Chagin	u_int delta, scale_bits;
1729931033bSDmitry Chagin	int error;
1739931033bSDmitry Chagin
1749931033bSDmitry Chagin	do {
1759931033bSDmitry Chagin		if (!tk->tk_enabled)
1769931033bSDmitry Chagin			return (ENOSYS);
1779931033bSDmitry Chagin
1789931033bSDmitry Chagin		curr = atomic_load_acq_32(&tk->tk_current);
1799931033bSDmitry Chagin		th = &tk->tk_th[curr];
1809931033bSDmitry Chagin		gen = atomic_load_acq_32(&th->th_gen);
1819931033bSDmitry Chagin		*bt = th->th_offset;
1829931033bSDmitry Chagin		error = tc_delta(th, &delta);
1839931033bSDmitry Chagin		if (error == EAGAIN)
1849931033bSDmitry Chagin			continue;
1859931033bSDmitry Chagin		if (error != 0)
1869931033bSDmitry Chagin			return (error);
1879931033bSDmitry Chagin		scale = th->th_scale;
1889931033bSDmitry Chagin#ifdef _LP64
1899931033bSDmitry Chagin		scale_bits = ffsl(scale);
1909931033bSDmitry Chagin#else
1919931033bSDmitry Chagin		scale_bits = ffsll(scale);
1929931033bSDmitry Chagin#endif
1939931033bSDmitry Chagin		if (__predict_false(scale_bits + fls(delta) > 63)) {
1949931033bSDmitry Chagin			x = (scale >> 32) * delta;
1959931033bSDmitry Chagin			scale &= 0xffffffff;
1969931033bSDmitry Chagin			bt->sec += x >> 32;
1979931033bSDmitry Chagin			bintime_addx(bt, x << 32);
1989931033bSDmitry Chagin		}
1999931033bSDmitry Chagin		bintime_addx(bt, scale * delta);
2009931033bSDmitry Chagin		if (abs)
2019931033bSDmitry Chagin			bintime_add(bt, &th->th_boottime);
2029931033bSDmitry Chagin
2039931033bSDmitry Chagin		/*
2049931033bSDmitry Chagin		 * Ensure that the load of th_offset is completed
2059931033bSDmitry Chagin		 * before the load of th_gen.
2069931033bSDmitry Chagin		 */
2079931033bSDmitry Chagin		atomic_thread_fence_acq();
2089931033bSDmitry Chagin	} while (curr != tk->tk_current || gen == 0 || gen != th->th_gen);
2099931033bSDmitry Chagin	return (0);
2109931033bSDmitry Chagin}
2119931033bSDmitry Chagin
2129931033bSDmitry Chaginstatic int
2139931033bSDmitry Chaginfreebsd_getnanouptime(struct bintime *bt, struct vdso_timekeep *tk)
2149931033bSDmitry Chagin{
2159931033bSDmitry Chagin	struct vdso_timehands *th;
2169931033bSDmitry Chagin	uint32_t curr, gen;
2179931033bSDmitry Chagin
2189931033bSDmitry Chagin	do {
2199931033bSDmitry Chagin		if (!tk->tk_enabled)
2209931033bSDmitry Chagin			return (ENOSYS);
2219931033bSDmitry Chagin
2229931033bSDmitry Chagin		curr = atomic_load_acq_32(&tk->tk_current);
2239931033bSDmitry Chagin		th = &tk->tk_th[curr];
2249931033bSDmitry Chagin		gen = atomic_load_acq_32(&th->th_gen);
2259931033bSDmitry Chagin		*bt = th->th_offset;
2269931033bSDmitry Chagin
2279931033bSDmitry Chagin		/*
2289931033bSDmitry Chagin		 * Ensure that the load of th_offset is completed
2299931033bSDmitry Chagin		 * before the load of th_gen.
2309931033bSDmitry Chagin		 */
2319931033bSDmitry Chagin		atomic_thread_fence_acq();
2329931033bSDmitry Chagin	} while (curr != tk->tk_current || gen == 0 || gen != th->th_gen);
2339931033bSDmitry Chagin	return (0);
2349931033bSDmitry Chagin}
2359931033bSDmitry Chagin
2369931033bSDmitry Chaginstatic int
2379931033bSDmitry Chaginfreebsd_gettimeofday(struct timeval *tv, struct timezone *tz)
2389931033bSDmitry Chagin{
2399931033bSDmitry Chagin	struct vdso_timekeep *tk;
2409931033bSDmitry Chagin	struct bintime bt;
2419931033bSDmitry Chagin	int error;
2429931033bSDmitry Chagin
2439931033bSDmitry Chagin	if (tz != NULL)
2449931033bSDmitry Chagin		return (ENOSYS);
2459931033bSDmitry Chagin	__vdso_gettimekeep(&tk);
2469931033bSDmitry Chagin	if (tk == NULL)
2479931033bSDmitry Chagin		return (ENOSYS);
2489931033bSDmitry Chagin	if (tk->tk_ver != VDSO_TK_VER_CURR)
2499931033bSDmitry Chagin		return (ENOSYS);
2509931033bSDmitry Chagin	error = freebsd_binuptime(&bt, tk, true);
2519931033bSDmitry Chagin	if (error == 0)
2529931033bSDmitry Chagin		bintime2timeval(&bt, tv);
2539931033bSDmitry Chagin	return (error);
2549931033bSDmitry Chagin}
2559931033bSDmitry Chagin
2569931033bSDmitry Chaginstatic int
2579931033bSDmitry Chaginfreebsd_clock_gettime(clockid_t clock_id, struct timespec *ts)
2589931033bSDmitry Chagin{
2599931033bSDmitry Chagin	struct vdso_timekeep *tk;
2609931033bSDmitry Chagin	struct bintime bt;
2619931033bSDmitry Chagin	int error;
2629931033bSDmitry Chagin
2639931033bSDmitry Chagin	__vdso_gettimekeep(&tk);
2649931033bSDmitry Chagin	if (tk == NULL)
2659931033bSDmitry Chagin		return (ENOSYS);
2669931033bSDmitry Chagin	if (tk->tk_ver != VDSO_TK_VER_CURR)
2679931033bSDmitry Chagin		return (ENOSYS);
2689931033bSDmitry Chagin	switch (clock_id) {
2699931033bSDmitry Chagin	case CLOCK_REALTIME:
2709931033bSDmitry Chagin	case CLOCK_REALTIME_PRECISE:
2719931033bSDmitry Chagin	case CLOCK_REALTIME_FAST:
2729931033bSDmitry Chagin		error = freebsd_binuptime(&bt, tk, true);
2739931033bSDmitry Chagin		break;
2749931033bSDmitry Chagin	case CLOCK_MONOTONIC:
2759931033bSDmitry Chagin	case CLOCK_MONOTONIC_PRECISE:
2769931033bSDmitry Chagin	case CLOCK_UPTIME:
2779931033bSDmitry Chagin	case CLOCK_UPTIME_PRECISE:
2789931033bSDmitry Chagin		error = freebsd_binuptime(&bt, tk, false);
2799931033bSDmitry Chagin		break;
2809931033bSDmitry Chagin	case CLOCK_MONOTONIC_FAST:
2819931033bSDmitry Chagin	case CLOCK_UPTIME_FAST:
2829931033bSDmitry Chagin		error = freebsd_getnanouptime(&bt, tk);
2839931033bSDmitry Chagin		break;
2849931033bSDmitry Chagin	default:
2859931033bSDmitry Chagin		error = ENOSYS;
2869931033bSDmitry Chagin		break;
2879931033bSDmitry Chagin	}
2889931033bSDmitry Chagin	if (error == 0)
2899931033bSDmitry Chagin		bintime2timespec(&bt, ts);
2909931033bSDmitry Chagin	return (error);
2919931033bSDmitry Chagin}
2929931033bSDmitry Chagin
2939931033bSDmitry Chagin/*
2949931033bSDmitry Chagin * Linux vDSO interfaces
2959931033bSDmitry Chagin *
2969931033bSDmitry Chagin */
2979931033bSDmitry Chaginint
2989931033bSDmitry Chagin__vdso_clock_gettime(clockid_t clock_id, struct l_timespec *lts)
2999931033bSDmitry Chagin{
3009931033bSDmitry Chagin	struct timespec ts;
3019931033bSDmitry Chagin	clockid_t which;
3029931033bSDmitry Chagin	int error;
3039931033bSDmitry Chagin
3049931033bSDmitry Chagin	error = __vdso_linux_to_native_clockid(&which, clock_id);
3059931033bSDmitry Chagin	if (error != 0)
3069931033bSDmitry Chagin		return (__vdso_clock_gettime_fallback(clock_id, lts));
3079931033bSDmitry Chagin	error = freebsd_clock_gettime(which, &ts);
3089931033bSDmitry Chagin	if (error == 0)
3099931033bSDmitry Chagin		return (-__vdso_native_to_linux_timespec(lts, &ts));
3109931033bSDmitry Chagin	else
3119931033bSDmitry Chagin		return (__vdso_clock_gettime_fallback(clock_id, lts));
3129931033bSDmitry Chagin}
3139931033bSDmitry Chagin
3149931033bSDmitry Chaginint
3159931033bSDmitry Chagin__vdso_gettimeofday(l_timeval *ltv, struct timezone *tz)
3169931033bSDmitry Chagin{
3179931033bSDmitry Chagin	struct timeval tv;
3189931033bSDmitry Chagin	int error;
3199931033bSDmitry Chagin
3209931033bSDmitry Chagin	error = freebsd_gettimeofday(&tv, tz);
3219931033bSDmitry Chagin	if (error != 0)
3229931033bSDmitry Chagin		return (__vdso_gettimeofday_fallback(ltv, tz));
3239931033bSDmitry Chagin	return (-__vdso_native_to_linux_timeval(ltv, &tv));
3249931033bSDmitry Chagin}
3259931033bSDmitry Chagin
3269931033bSDmitry Chaginint
3279931033bSDmitry Chagin__vdso_clock_getres(clockid_t clock_id, struct l_timespec *lts)
3289931033bSDmitry Chagin{
3299931033bSDmitry Chagin
3309931033bSDmitry Chagin	return (__vdso_clock_getres_fallback(clock_id, lts));
3319931033bSDmitry Chagin}
3329931033bSDmitry Chagin
3339931033bSDmitry Chagin#if defined(__i386__) || defined(COMPAT_LINUX32)
3349931033bSDmitry Chaginint
3359931033bSDmitry Chagin__vdso_clock_gettime64(clockid_t clock_id, struct l_timespec64 *lts)
3369931033bSDmitry Chagin{
3379931033bSDmitry Chagin	struct timespec ts;
3389931033bSDmitry Chagin	clockid_t which;
3399931033bSDmitry Chagin	int error;
3409931033bSDmitry Chagin
3419931033bSDmitry Chagin	error = __vdso_linux_to_native_clockid(&which, clock_id);
3429931033bSDmitry Chagin	if (error != 0)
3439931033bSDmitry Chagin		return (__vdso_clock_gettime64_fallback(clock_id, lts));
3449931033bSDmitry Chagin	error = freebsd_clock_gettime(which, &ts);
3459931033bSDmitry Chagin	if (error == 0)
3469931033bSDmitry Chagin		return(-__vdso_native_to_linux_timespec64(lts, &ts));
3479931033bSDmitry Chagin	else
3489931033bSDmitry Chagin		return(__vdso_clock_gettime64_fallback(clock_id, lts));
3499931033bSDmitry Chagin}
3509931033bSDmitry Chagin
3519931033bSDmitry Chaginint clock_gettime64(clockid_t clock_id, struct l_timespec64 *lts)
3529931033bSDmitry Chagin    __attribute__((weak, alias("__vdso_clock_gettime64")));
3539931033bSDmitry Chagin#endif
3549931033bSDmitry Chagin
355*5a6a4fb2SDmitry Chagin#if defined(__i386__) || defined(__amd64__)
3569931033bSDmitry Chaginint
3579931033bSDmitry Chagin__vdso_getcpu(uint32_t *cpu, uint32_t *node, void *cache)
3589931033bSDmitry Chagin{
359*5a6a4fb2SDmitry Chagin	int ret;
3609931033bSDmitry Chagin
361*5a6a4fb2SDmitry Chagin	if (node != NULL)
3629931033bSDmitry Chagin		return (__vdso_getcpu_fallback(cpu, node, cache));
363*5a6a4fb2SDmitry Chagin	ret = __vdso_getcpu_try();
364*5a6a4fb2SDmitry Chagin	if (ret < 0)
365*5a6a4fb2SDmitry Chagin		return (__vdso_getcpu_fallback(cpu, node, cache));
366*5a6a4fb2SDmitry Chagin	*cpu = ret;
367*5a6a4fb2SDmitry Chagin	return (0);
3689931033bSDmitry Chagin}
3699931033bSDmitry Chagin#endif
3709931033bSDmitry Chagin
3719931033bSDmitry Chagin#if defined(__i386__) || defined(__amd64__)
3729931033bSDmitry Chaginint
3739931033bSDmitry Chagin__vdso_time(long *tm)
3749931033bSDmitry Chagin{
3759931033bSDmitry Chagin
3769931033bSDmitry Chagin	return (__vdso_time_fallback(tm));
3779931033bSDmitry Chagin}
3789931033bSDmitry Chagin#endif
379