18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright 2006 Andi Kleen, SUSE Labs.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Fast user context implementation of clock_gettime, gettimeofday, and time.
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * The code should have no internal unresolved relocations.
88c2ecf20Sopenharmony_ci * Check with readelf after changing.
98c2ecf20Sopenharmony_ci * Also alternative() doesn't work.
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci/*
128c2ecf20Sopenharmony_ci * Copyright (c) 2017 Oracle and/or its affiliates. All rights reserved.
138c2ecf20Sopenharmony_ci */
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include <linux/kernel.h>
168c2ecf20Sopenharmony_ci#include <linux/time.h>
178c2ecf20Sopenharmony_ci#include <linux/string.h>
188c2ecf20Sopenharmony_ci#include <asm/io.h>
198c2ecf20Sopenharmony_ci#include <asm/unistd.h>
208c2ecf20Sopenharmony_ci#include <asm/timex.h>
218c2ecf20Sopenharmony_ci#include <asm/clocksource.h>
228c2ecf20Sopenharmony_ci#include <asm/vvar.h>
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#ifdef	CONFIG_SPARC64
258c2ecf20Sopenharmony_ci#define SYSCALL_STRING							\
268c2ecf20Sopenharmony_ci	"ta	0x6d;"							\
278c2ecf20Sopenharmony_ci	"bcs,a	1f;"							\
288c2ecf20Sopenharmony_ci	" sub	%%g0, %%o0, %%o0;"					\
298c2ecf20Sopenharmony_ci	"1:"
308c2ecf20Sopenharmony_ci#else
318c2ecf20Sopenharmony_ci#define SYSCALL_STRING							\
328c2ecf20Sopenharmony_ci	"ta	0x10;"							\
338c2ecf20Sopenharmony_ci	"bcs,a	1f;"							\
348c2ecf20Sopenharmony_ci	" sub	%%g0, %%o0, %%o0;"					\
358c2ecf20Sopenharmony_ci	"1:"
368c2ecf20Sopenharmony_ci#endif
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci#define SYSCALL_CLOBBERS						\
398c2ecf20Sopenharmony_ci	"f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7",			\
408c2ecf20Sopenharmony_ci	"f8", "f9", "f10", "f11", "f12", "f13", "f14", "f15",		\
418c2ecf20Sopenharmony_ci	"f16", "f17", "f18", "f19", "f20", "f21", "f22", "f23",		\
428c2ecf20Sopenharmony_ci	"f24", "f25", "f26", "f27", "f28", "f29", "f30", "f31",		\
438c2ecf20Sopenharmony_ci	"f32", "f34", "f36", "f38", "f40", "f42", "f44", "f46",		\
448c2ecf20Sopenharmony_ci	"f48", "f50", "f52", "f54", "f56", "f58", "f60", "f62",		\
458c2ecf20Sopenharmony_ci	"cc", "memory"
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci/*
488c2ecf20Sopenharmony_ci * Compute the vvar page's address in the process address space, and return it
498c2ecf20Sopenharmony_ci * as a pointer to the vvar_data.
508c2ecf20Sopenharmony_ci */
518c2ecf20Sopenharmony_cinotrace static __always_inline struct vvar_data *get_vvar_data(void)
528c2ecf20Sopenharmony_ci{
538c2ecf20Sopenharmony_ci	unsigned long ret;
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	/*
568c2ecf20Sopenharmony_ci	 * vdso data page is the first vDSO page so grab the PC
578c2ecf20Sopenharmony_ci	 * and move up a page to get to the data page.
588c2ecf20Sopenharmony_ci	 */
598c2ecf20Sopenharmony_ci	__asm__("rd %%pc, %0" : "=r" (ret));
608c2ecf20Sopenharmony_ci	ret &= ~(8192 - 1);
618c2ecf20Sopenharmony_ci	ret -= 8192;
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	return (struct vvar_data *) ret;
648c2ecf20Sopenharmony_ci}
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_cinotrace static long vdso_fallback_gettime(long clock, struct __kernel_old_timespec *ts)
678c2ecf20Sopenharmony_ci{
688c2ecf20Sopenharmony_ci	register long num __asm__("g1") = __NR_clock_gettime;
698c2ecf20Sopenharmony_ci	register long o0 __asm__("o0") = clock;
708c2ecf20Sopenharmony_ci	register long o1 __asm__("o1") = (long) ts;
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	__asm__ __volatile__(SYSCALL_STRING : "=r" (o0) : "r" (num),
738c2ecf20Sopenharmony_ci			     "0" (o0), "r" (o1) : SYSCALL_CLOBBERS);
748c2ecf20Sopenharmony_ci	return o0;
758c2ecf20Sopenharmony_ci}
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_cinotrace static long vdso_fallback_gettimeofday(struct __kernel_old_timeval *tv, struct timezone *tz)
788c2ecf20Sopenharmony_ci{
798c2ecf20Sopenharmony_ci	register long num __asm__("g1") = __NR_gettimeofday;
808c2ecf20Sopenharmony_ci	register long o0 __asm__("o0") = (long) tv;
818c2ecf20Sopenharmony_ci	register long o1 __asm__("o1") = (long) tz;
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	__asm__ __volatile__(SYSCALL_STRING : "=r" (o0) : "r" (num),
848c2ecf20Sopenharmony_ci			     "0" (o0), "r" (o1) : SYSCALL_CLOBBERS);
858c2ecf20Sopenharmony_ci	return o0;
868c2ecf20Sopenharmony_ci}
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci#ifdef	CONFIG_SPARC64
898c2ecf20Sopenharmony_cinotrace static __always_inline u64 vread_tick(void)
908c2ecf20Sopenharmony_ci{
918c2ecf20Sopenharmony_ci	u64	ret;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	__asm__ __volatile__("rd %%tick, %0" : "=r" (ret));
948c2ecf20Sopenharmony_ci	return ret;
958c2ecf20Sopenharmony_ci}
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_cinotrace static __always_inline u64 vread_tick_stick(void)
988c2ecf20Sopenharmony_ci{
998c2ecf20Sopenharmony_ci	u64	ret;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	__asm__ __volatile__("rd %%asr24, %0" : "=r" (ret));
1028c2ecf20Sopenharmony_ci	return ret;
1038c2ecf20Sopenharmony_ci}
1048c2ecf20Sopenharmony_ci#else
1058c2ecf20Sopenharmony_cinotrace static __always_inline u64 vread_tick(void)
1068c2ecf20Sopenharmony_ci{
1078c2ecf20Sopenharmony_ci	register unsigned long long ret asm("o4");
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	__asm__ __volatile__("rd %%tick, %L0\n\t"
1108c2ecf20Sopenharmony_ci			     "srlx %L0, 32, %H0"
1118c2ecf20Sopenharmony_ci			     : "=r" (ret));
1128c2ecf20Sopenharmony_ci	return ret;
1138c2ecf20Sopenharmony_ci}
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_cinotrace static __always_inline u64 vread_tick_stick(void)
1168c2ecf20Sopenharmony_ci{
1178c2ecf20Sopenharmony_ci	register unsigned long long ret asm("o4");
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	__asm__ __volatile__("rd %%asr24, %L0\n\t"
1208c2ecf20Sopenharmony_ci			     "srlx %L0, 32, %H0"
1218c2ecf20Sopenharmony_ci			     : "=r" (ret));
1228c2ecf20Sopenharmony_ci	return ret;
1238c2ecf20Sopenharmony_ci}
1248c2ecf20Sopenharmony_ci#endif
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_cinotrace static __always_inline u64 vgetsns(struct vvar_data *vvar)
1278c2ecf20Sopenharmony_ci{
1288c2ecf20Sopenharmony_ci	u64 v;
1298c2ecf20Sopenharmony_ci	u64 cycles;
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	cycles = vread_tick();
1328c2ecf20Sopenharmony_ci	v = (cycles - vvar->clock.cycle_last) & vvar->clock.mask;
1338c2ecf20Sopenharmony_ci	return v * vvar->clock.mult;
1348c2ecf20Sopenharmony_ci}
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_cinotrace static __always_inline u64 vgetsns_stick(struct vvar_data *vvar)
1378c2ecf20Sopenharmony_ci{
1388c2ecf20Sopenharmony_ci	u64 v;
1398c2ecf20Sopenharmony_ci	u64 cycles;
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	cycles = vread_tick_stick();
1428c2ecf20Sopenharmony_ci	v = (cycles - vvar->clock.cycle_last) & vvar->clock.mask;
1438c2ecf20Sopenharmony_ci	return v * vvar->clock.mult;
1448c2ecf20Sopenharmony_ci}
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_cinotrace static __always_inline int do_realtime(struct vvar_data *vvar,
1478c2ecf20Sopenharmony_ci					       struct __kernel_old_timespec *ts)
1488c2ecf20Sopenharmony_ci{
1498c2ecf20Sopenharmony_ci	unsigned long seq;
1508c2ecf20Sopenharmony_ci	u64 ns;
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	do {
1538c2ecf20Sopenharmony_ci		seq = vvar_read_begin(vvar);
1548c2ecf20Sopenharmony_ci		ts->tv_sec = vvar->wall_time_sec;
1558c2ecf20Sopenharmony_ci		ns = vvar->wall_time_snsec;
1568c2ecf20Sopenharmony_ci		ns += vgetsns(vvar);
1578c2ecf20Sopenharmony_ci		ns >>= vvar->clock.shift;
1588c2ecf20Sopenharmony_ci	} while (unlikely(vvar_read_retry(vvar, seq)));
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	ts->tv_sec += __iter_div_u64_rem(ns, NSEC_PER_SEC, &ns);
1618c2ecf20Sopenharmony_ci	ts->tv_nsec = ns;
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	return 0;
1648c2ecf20Sopenharmony_ci}
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_cinotrace static __always_inline int do_realtime_stick(struct vvar_data *vvar,
1678c2ecf20Sopenharmony_ci						     struct __kernel_old_timespec *ts)
1688c2ecf20Sopenharmony_ci{
1698c2ecf20Sopenharmony_ci	unsigned long seq;
1708c2ecf20Sopenharmony_ci	u64 ns;
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	do {
1738c2ecf20Sopenharmony_ci		seq = vvar_read_begin(vvar);
1748c2ecf20Sopenharmony_ci		ts->tv_sec = vvar->wall_time_sec;
1758c2ecf20Sopenharmony_ci		ns = vvar->wall_time_snsec;
1768c2ecf20Sopenharmony_ci		ns += vgetsns_stick(vvar);
1778c2ecf20Sopenharmony_ci		ns >>= vvar->clock.shift;
1788c2ecf20Sopenharmony_ci	} while (unlikely(vvar_read_retry(vvar, seq)));
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	ts->tv_sec += __iter_div_u64_rem(ns, NSEC_PER_SEC, &ns);
1818c2ecf20Sopenharmony_ci	ts->tv_nsec = ns;
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	return 0;
1848c2ecf20Sopenharmony_ci}
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_cinotrace static __always_inline int do_monotonic(struct vvar_data *vvar,
1878c2ecf20Sopenharmony_ci						struct __kernel_old_timespec *ts)
1888c2ecf20Sopenharmony_ci{
1898c2ecf20Sopenharmony_ci	unsigned long seq;
1908c2ecf20Sopenharmony_ci	u64 ns;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	do {
1938c2ecf20Sopenharmony_ci		seq = vvar_read_begin(vvar);
1948c2ecf20Sopenharmony_ci		ts->tv_sec = vvar->monotonic_time_sec;
1958c2ecf20Sopenharmony_ci		ns = vvar->monotonic_time_snsec;
1968c2ecf20Sopenharmony_ci		ns += vgetsns(vvar);
1978c2ecf20Sopenharmony_ci		ns >>= vvar->clock.shift;
1988c2ecf20Sopenharmony_ci	} while (unlikely(vvar_read_retry(vvar, seq)));
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	ts->tv_sec += __iter_div_u64_rem(ns, NSEC_PER_SEC, &ns);
2018c2ecf20Sopenharmony_ci	ts->tv_nsec = ns;
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	return 0;
2048c2ecf20Sopenharmony_ci}
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_cinotrace static __always_inline int do_monotonic_stick(struct vvar_data *vvar,
2078c2ecf20Sopenharmony_ci						      struct __kernel_old_timespec *ts)
2088c2ecf20Sopenharmony_ci{
2098c2ecf20Sopenharmony_ci	unsigned long seq;
2108c2ecf20Sopenharmony_ci	u64 ns;
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	do {
2138c2ecf20Sopenharmony_ci		seq = vvar_read_begin(vvar);
2148c2ecf20Sopenharmony_ci		ts->tv_sec = vvar->monotonic_time_sec;
2158c2ecf20Sopenharmony_ci		ns = vvar->monotonic_time_snsec;
2168c2ecf20Sopenharmony_ci		ns += vgetsns_stick(vvar);
2178c2ecf20Sopenharmony_ci		ns >>= vvar->clock.shift;
2188c2ecf20Sopenharmony_ci	} while (unlikely(vvar_read_retry(vvar, seq)));
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	ts->tv_sec += __iter_div_u64_rem(ns, NSEC_PER_SEC, &ns);
2218c2ecf20Sopenharmony_ci	ts->tv_nsec = ns;
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	return 0;
2248c2ecf20Sopenharmony_ci}
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_cinotrace static int do_realtime_coarse(struct vvar_data *vvar,
2278c2ecf20Sopenharmony_ci				      struct __kernel_old_timespec *ts)
2288c2ecf20Sopenharmony_ci{
2298c2ecf20Sopenharmony_ci	unsigned long seq;
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	do {
2328c2ecf20Sopenharmony_ci		seq = vvar_read_begin(vvar);
2338c2ecf20Sopenharmony_ci		ts->tv_sec = vvar->wall_time_coarse_sec;
2348c2ecf20Sopenharmony_ci		ts->tv_nsec = vvar->wall_time_coarse_nsec;
2358c2ecf20Sopenharmony_ci	} while (unlikely(vvar_read_retry(vvar, seq)));
2368c2ecf20Sopenharmony_ci	return 0;
2378c2ecf20Sopenharmony_ci}
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_cinotrace static int do_monotonic_coarse(struct vvar_data *vvar,
2408c2ecf20Sopenharmony_ci				       struct __kernel_old_timespec *ts)
2418c2ecf20Sopenharmony_ci{
2428c2ecf20Sopenharmony_ci	unsigned long seq;
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	do {
2458c2ecf20Sopenharmony_ci		seq = vvar_read_begin(vvar);
2468c2ecf20Sopenharmony_ci		ts->tv_sec = vvar->monotonic_time_coarse_sec;
2478c2ecf20Sopenharmony_ci		ts->tv_nsec = vvar->monotonic_time_coarse_nsec;
2488c2ecf20Sopenharmony_ci	} while (unlikely(vvar_read_retry(vvar, seq)));
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	return 0;
2518c2ecf20Sopenharmony_ci}
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_cinotrace int
2548c2ecf20Sopenharmony_ci__vdso_clock_gettime(clockid_t clock, struct __kernel_old_timespec *ts)
2558c2ecf20Sopenharmony_ci{
2568c2ecf20Sopenharmony_ci	struct vvar_data *vvd = get_vvar_data();
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	switch (clock) {
2598c2ecf20Sopenharmony_ci	case CLOCK_REALTIME:
2608c2ecf20Sopenharmony_ci		if (unlikely(vvd->vclock_mode == VCLOCK_NONE))
2618c2ecf20Sopenharmony_ci			break;
2628c2ecf20Sopenharmony_ci		return do_realtime(vvd, ts);
2638c2ecf20Sopenharmony_ci	case CLOCK_MONOTONIC:
2648c2ecf20Sopenharmony_ci		if (unlikely(vvd->vclock_mode == VCLOCK_NONE))
2658c2ecf20Sopenharmony_ci			break;
2668c2ecf20Sopenharmony_ci		return do_monotonic(vvd, ts);
2678c2ecf20Sopenharmony_ci	case CLOCK_REALTIME_COARSE:
2688c2ecf20Sopenharmony_ci		return do_realtime_coarse(vvd, ts);
2698c2ecf20Sopenharmony_ci	case CLOCK_MONOTONIC_COARSE:
2708c2ecf20Sopenharmony_ci		return do_monotonic_coarse(vvd, ts);
2718c2ecf20Sopenharmony_ci	}
2728c2ecf20Sopenharmony_ci	/*
2738c2ecf20Sopenharmony_ci	 * Unknown clock ID ? Fall back to the syscall.
2748c2ecf20Sopenharmony_ci	 */
2758c2ecf20Sopenharmony_ci	return vdso_fallback_gettime(clock, ts);
2768c2ecf20Sopenharmony_ci}
2778c2ecf20Sopenharmony_ciint
2788c2ecf20Sopenharmony_ciclock_gettime(clockid_t, struct __kernel_old_timespec *)
2798c2ecf20Sopenharmony_ci	__attribute__((weak, alias("__vdso_clock_gettime")));
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_cinotrace int
2828c2ecf20Sopenharmony_ci__vdso_clock_gettime_stick(clockid_t clock, struct __kernel_old_timespec *ts)
2838c2ecf20Sopenharmony_ci{
2848c2ecf20Sopenharmony_ci	struct vvar_data *vvd = get_vvar_data();
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	switch (clock) {
2878c2ecf20Sopenharmony_ci	case CLOCK_REALTIME:
2888c2ecf20Sopenharmony_ci		if (unlikely(vvd->vclock_mode == VCLOCK_NONE))
2898c2ecf20Sopenharmony_ci			break;
2908c2ecf20Sopenharmony_ci		return do_realtime_stick(vvd, ts);
2918c2ecf20Sopenharmony_ci	case CLOCK_MONOTONIC:
2928c2ecf20Sopenharmony_ci		if (unlikely(vvd->vclock_mode == VCLOCK_NONE))
2938c2ecf20Sopenharmony_ci			break;
2948c2ecf20Sopenharmony_ci		return do_monotonic_stick(vvd, ts);
2958c2ecf20Sopenharmony_ci	case CLOCK_REALTIME_COARSE:
2968c2ecf20Sopenharmony_ci		return do_realtime_coarse(vvd, ts);
2978c2ecf20Sopenharmony_ci	case CLOCK_MONOTONIC_COARSE:
2988c2ecf20Sopenharmony_ci		return do_monotonic_coarse(vvd, ts);
2998c2ecf20Sopenharmony_ci	}
3008c2ecf20Sopenharmony_ci	/*
3018c2ecf20Sopenharmony_ci	 * Unknown clock ID ? Fall back to the syscall.
3028c2ecf20Sopenharmony_ci	 */
3038c2ecf20Sopenharmony_ci	return vdso_fallback_gettime(clock, ts);
3048c2ecf20Sopenharmony_ci}
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_cinotrace int
3078c2ecf20Sopenharmony_ci__vdso_gettimeofday(struct __kernel_old_timeval *tv, struct timezone *tz)
3088c2ecf20Sopenharmony_ci{
3098c2ecf20Sopenharmony_ci	struct vvar_data *vvd = get_vvar_data();
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	if (likely(vvd->vclock_mode != VCLOCK_NONE)) {
3128c2ecf20Sopenharmony_ci		if (likely(tv != NULL)) {
3138c2ecf20Sopenharmony_ci			union tstv_t {
3148c2ecf20Sopenharmony_ci				struct __kernel_old_timespec ts;
3158c2ecf20Sopenharmony_ci				struct __kernel_old_timeval tv;
3168c2ecf20Sopenharmony_ci			} *tstv = (union tstv_t *) tv;
3178c2ecf20Sopenharmony_ci			do_realtime(vvd, &tstv->ts);
3188c2ecf20Sopenharmony_ci			/*
3198c2ecf20Sopenharmony_ci			 * Assign before dividing to ensure that the division is
3208c2ecf20Sopenharmony_ci			 * done in the type of tv_usec, not tv_nsec.
3218c2ecf20Sopenharmony_ci			 *
3228c2ecf20Sopenharmony_ci			 * There cannot be > 1 billion usec in a second:
3238c2ecf20Sopenharmony_ci			 * do_realtime() has already distributed such overflow
3248c2ecf20Sopenharmony_ci			 * into tv_sec.  So we can assign it to an int safely.
3258c2ecf20Sopenharmony_ci			 */
3268c2ecf20Sopenharmony_ci			tstv->tv.tv_usec = tstv->ts.tv_nsec;
3278c2ecf20Sopenharmony_ci			tstv->tv.tv_usec /= 1000;
3288c2ecf20Sopenharmony_ci		}
3298c2ecf20Sopenharmony_ci		if (unlikely(tz != NULL)) {
3308c2ecf20Sopenharmony_ci			/* Avoid memcpy. Some old compilers fail to inline it */
3318c2ecf20Sopenharmony_ci			tz->tz_minuteswest = vvd->tz_minuteswest;
3328c2ecf20Sopenharmony_ci			tz->tz_dsttime = vvd->tz_dsttime;
3338c2ecf20Sopenharmony_ci		}
3348c2ecf20Sopenharmony_ci		return 0;
3358c2ecf20Sopenharmony_ci	}
3368c2ecf20Sopenharmony_ci	return vdso_fallback_gettimeofday(tv, tz);
3378c2ecf20Sopenharmony_ci}
3388c2ecf20Sopenharmony_ciint
3398c2ecf20Sopenharmony_cigettimeofday(struct __kernel_old_timeval *, struct timezone *)
3408c2ecf20Sopenharmony_ci	__attribute__((weak, alias("__vdso_gettimeofday")));
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_cinotrace int
3438c2ecf20Sopenharmony_ci__vdso_gettimeofday_stick(struct __kernel_old_timeval *tv, struct timezone *tz)
3448c2ecf20Sopenharmony_ci{
3458c2ecf20Sopenharmony_ci	struct vvar_data *vvd = get_vvar_data();
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	if (likely(vvd->vclock_mode != VCLOCK_NONE)) {
3488c2ecf20Sopenharmony_ci		if (likely(tv != NULL)) {
3498c2ecf20Sopenharmony_ci			union tstv_t {
3508c2ecf20Sopenharmony_ci				struct __kernel_old_timespec ts;
3518c2ecf20Sopenharmony_ci				struct __kernel_old_timeval tv;
3528c2ecf20Sopenharmony_ci			} *tstv = (union tstv_t *) tv;
3538c2ecf20Sopenharmony_ci			do_realtime_stick(vvd, &tstv->ts);
3548c2ecf20Sopenharmony_ci			/*
3558c2ecf20Sopenharmony_ci			 * Assign before dividing to ensure that the division is
3568c2ecf20Sopenharmony_ci			 * done in the type of tv_usec, not tv_nsec.
3578c2ecf20Sopenharmony_ci			 *
3588c2ecf20Sopenharmony_ci			 * There cannot be > 1 billion usec in a second:
3598c2ecf20Sopenharmony_ci			 * do_realtime() has already distributed such overflow
3608c2ecf20Sopenharmony_ci			 * into tv_sec.  So we can assign it to an int safely.
3618c2ecf20Sopenharmony_ci			 */
3628c2ecf20Sopenharmony_ci			tstv->tv.tv_usec = tstv->ts.tv_nsec;
3638c2ecf20Sopenharmony_ci			tstv->tv.tv_usec /= 1000;
3648c2ecf20Sopenharmony_ci		}
3658c2ecf20Sopenharmony_ci		if (unlikely(tz != NULL)) {
3668c2ecf20Sopenharmony_ci			/* Avoid memcpy. Some old compilers fail to inline it */
3678c2ecf20Sopenharmony_ci			tz->tz_minuteswest = vvd->tz_minuteswest;
3688c2ecf20Sopenharmony_ci			tz->tz_dsttime = vvd->tz_dsttime;
3698c2ecf20Sopenharmony_ci		}
3708c2ecf20Sopenharmony_ci		return 0;
3718c2ecf20Sopenharmony_ci	}
3728c2ecf20Sopenharmony_ci	return vdso_fallback_gettimeofday(tv, tz);
3738c2ecf20Sopenharmony_ci}
374