xref: /openbmc/linux/kernel/time/vsyscall.c (revision 8be98d2f2a0a262f8bf8a0bc1fdf522b3c7aab17)
144f57d78SVincenzo Frascino // SPDX-License-Identifier: GPL-2.0
244f57d78SVincenzo Frascino /*
344f57d78SVincenzo Frascino  * Copyright 2019 ARM Ltd.
444f57d78SVincenzo Frascino  *
544f57d78SVincenzo Frascino  * Generic implementation of update_vsyscall and update_vsyscall_tz.
644f57d78SVincenzo Frascino  *
744f57d78SVincenzo Frascino  * Based on the x86 specific implementation.
844f57d78SVincenzo Frascino  */
944f57d78SVincenzo Frascino 
1044f57d78SVincenzo Frascino #include <linux/hrtimer.h>
1144f57d78SVincenzo Frascino #include <linux/timekeeper_internal.h>
1244f57d78SVincenzo Frascino #include <vdso/datapage.h>
1344f57d78SVincenzo Frascino #include <vdso/helpers.h>
1444f57d78SVincenzo Frascino #include <vdso/vsyscall.h>
1544f57d78SVincenzo Frascino 
1619d0070aSThomas Gleixner #include "timekeeping_internal.h"
1719d0070aSThomas Gleixner 
update_vdso_data(struct vdso_data * vdata,struct timekeeper * tk)1844f57d78SVincenzo Frascino static inline void update_vdso_data(struct vdso_data *vdata,
1944f57d78SVincenzo Frascino 				    struct timekeeper *tk)
2044f57d78SVincenzo Frascino {
2144f57d78SVincenzo Frascino 	struct vdso_timestamp *vdso_ts;
22b99328a6SThomas Gleixner 	u64 nsec, sec;
2344f57d78SVincenzo Frascino 
2444f57d78SVincenzo Frascino 	vdata[CS_HRES_COARSE].cycle_last	= tk->tkr_mono.cycle_last;
2544f57d78SVincenzo Frascino 	vdata[CS_HRES_COARSE].mask		= tk->tkr_mono.mask;
2644f57d78SVincenzo Frascino 	vdata[CS_HRES_COARSE].mult		= tk->tkr_mono.mult;
2744f57d78SVincenzo Frascino 	vdata[CS_HRES_COARSE].shift		= tk->tkr_mono.shift;
2844f57d78SVincenzo Frascino 	vdata[CS_RAW].cycle_last		= tk->tkr_raw.cycle_last;
2944f57d78SVincenzo Frascino 	vdata[CS_RAW].mask			= tk->tkr_raw.mask;
3044f57d78SVincenzo Frascino 	vdata[CS_RAW].mult			= tk->tkr_raw.mult;
3144f57d78SVincenzo Frascino 	vdata[CS_RAW].shift			= tk->tkr_raw.shift;
3244f57d78SVincenzo Frascino 
3344f57d78SVincenzo Frascino 	/* CLOCK_MONOTONIC */
3444f57d78SVincenzo Frascino 	vdso_ts		= &vdata[CS_HRES_COARSE].basetime[CLOCK_MONOTONIC];
3544f57d78SVincenzo Frascino 	vdso_ts->sec	= tk->xtime_sec + tk->wall_to_monotonic.tv_sec;
3644f57d78SVincenzo Frascino 
3744f57d78SVincenzo Frascino 	nsec = tk->tkr_mono.xtime_nsec;
3844f57d78SVincenzo Frascino 	nsec += ((u64)tk->wall_to_monotonic.tv_nsec << tk->tkr_mono.shift);
3944f57d78SVincenzo Frascino 	while (nsec >= (((u64)NSEC_PER_SEC) << tk->tkr_mono.shift)) {
4044f57d78SVincenzo Frascino 		nsec -= (((u64)NSEC_PER_SEC) << tk->tkr_mono.shift);
4144f57d78SVincenzo Frascino 		vdso_ts->sec++;
4244f57d78SVincenzo Frascino 	}
4344f57d78SVincenzo Frascino 	vdso_ts->nsec	= nsec;
4444f57d78SVincenzo Frascino 
45b99328a6SThomas Gleixner 	/* Copy MONOTONIC time for BOOTTIME */
46b99328a6SThomas Gleixner 	sec	= vdso_ts->sec;
47b99328a6SThomas Gleixner 	/* Add the boot offset */
48b99328a6SThomas Gleixner 	sec	+= tk->monotonic_to_boot.tv_sec;
49b99328a6SThomas Gleixner 	nsec	+= (u64)tk->monotonic_to_boot.tv_nsec << tk->tkr_mono.shift;
5044f57d78SVincenzo Frascino 
5144f57d78SVincenzo Frascino 	/* CLOCK_BOOTTIME */
5244f57d78SVincenzo Frascino 	vdso_ts		= &vdata[CS_HRES_COARSE].basetime[CLOCK_BOOTTIME];
53b99328a6SThomas Gleixner 	vdso_ts->sec	= sec;
54b99328a6SThomas Gleixner 
5544f57d78SVincenzo Frascino 	while (nsec >= (((u64)NSEC_PER_SEC) << tk->tkr_mono.shift)) {
5644f57d78SVincenzo Frascino 		nsec -= (((u64)NSEC_PER_SEC) << tk->tkr_mono.shift);
5744f57d78SVincenzo Frascino 		vdso_ts->sec++;
5844f57d78SVincenzo Frascino 	}
5944f57d78SVincenzo Frascino 	vdso_ts->nsec	= nsec;
6044f57d78SVincenzo Frascino 
61b99328a6SThomas Gleixner 	/* CLOCK_MONOTONIC_RAW */
62b99328a6SThomas Gleixner 	vdso_ts		= &vdata[CS_RAW].basetime[CLOCK_MONOTONIC_RAW];
63b99328a6SThomas Gleixner 	vdso_ts->sec	= tk->raw_sec;
64b99328a6SThomas Gleixner 	vdso_ts->nsec	= tk->tkr_raw.xtime_nsec;
65b99328a6SThomas Gleixner 
6644f57d78SVincenzo Frascino 	/* CLOCK_TAI */
6744f57d78SVincenzo Frascino 	vdso_ts		= &vdata[CS_HRES_COARSE].basetime[CLOCK_TAI];
6844f57d78SVincenzo Frascino 	vdso_ts->sec	= tk->xtime_sec + (s64)tk->tai_offset;
6944f57d78SVincenzo Frascino 	vdso_ts->nsec	= tk->tkr_mono.xtime_nsec;
7044f57d78SVincenzo Frascino }
7144f57d78SVincenzo Frascino 
update_vsyscall(struct timekeeper * tk)7244f57d78SVincenzo Frascino void update_vsyscall(struct timekeeper *tk)
7344f57d78SVincenzo Frascino {
7444f57d78SVincenzo Frascino 	struct vdso_data *vdata = __arch_get_k_vdso_data();
7544f57d78SVincenzo Frascino 	struct vdso_timestamp *vdso_ts;
765d51bee7SThomas Gleixner 	s32 clock_mode;
7744f57d78SVincenzo Frascino 	u64 nsec;
7844f57d78SVincenzo Frascino 
7944f57d78SVincenzo Frascino 	/* copy vsyscall data */
8044f57d78SVincenzo Frascino 	vdso_write_begin(vdata);
8144f57d78SVincenzo Frascino 
825d51bee7SThomas Gleixner 	clock_mode = tk->tkr_mono.clock->vdso_clock_mode;
835d51bee7SThomas Gleixner 	vdata[CS_HRES_COARSE].clock_mode	= clock_mode;
845d51bee7SThomas Gleixner 	vdata[CS_RAW].clock_mode		= clock_mode;
8544f57d78SVincenzo Frascino 
869f24c540SThomas Gleixner 	/* CLOCK_REALTIME also required for time() */
879f24c540SThomas Gleixner 	vdso_ts		= &vdata[CS_HRES_COARSE].basetime[CLOCK_REALTIME];
889f24c540SThomas Gleixner 	vdso_ts->sec	= tk->xtime_sec;
899f24c540SThomas Gleixner 	vdso_ts->nsec	= tk->tkr_mono.xtime_nsec;
909f24c540SThomas Gleixner 
9144f57d78SVincenzo Frascino 	/* CLOCK_REALTIME_COARSE */
9244f57d78SVincenzo Frascino 	vdso_ts		= &vdata[CS_HRES_COARSE].basetime[CLOCK_REALTIME_COARSE];
9344f57d78SVincenzo Frascino 	vdso_ts->sec	= tk->xtime_sec;
9444f57d78SVincenzo Frascino 	vdso_ts->nsec	= tk->tkr_mono.xtime_nsec >> tk->tkr_mono.shift;
9544f57d78SVincenzo Frascino 
9644f57d78SVincenzo Frascino 	/* CLOCK_MONOTONIC_COARSE */
9744f57d78SVincenzo Frascino 	vdso_ts		= &vdata[CS_HRES_COARSE].basetime[CLOCK_MONOTONIC_COARSE];
9844f57d78SVincenzo Frascino 	vdso_ts->sec	= tk->xtime_sec + tk->wall_to_monotonic.tv_sec;
9944f57d78SVincenzo Frascino 	nsec		= tk->tkr_mono.xtime_nsec >> tk->tkr_mono.shift;
10044f57d78SVincenzo Frascino 	nsec		= nsec + tk->wall_to_monotonic.tv_nsec;
1010df1c986SArnd Bergmann 	vdso_ts->sec	+= __iter_div_u64_rem(nsec, NSEC_PER_SEC, &vdso_ts->nsec);
10244f57d78SVincenzo Frascino 
1039f24c540SThomas Gleixner 	/*
1049f24c540SThomas Gleixner 	 * Read without the seqlock held by clock_getres().
1059f24c540SThomas Gleixner 	 * Note: No need to have a second copy.
1069f24c540SThomas Gleixner 	 */
1079f24c540SThomas Gleixner 	WRITE_ONCE(vdata[CS_HRES_COARSE].hrtimer_res, hrtimer_resolution);
1089f24c540SThomas Gleixner 
1099f24c540SThomas Gleixner 	/*
110c7a18100SThomas Gleixner 	 * If the current clocksource is not VDSO capable, then spare the
111*4bf07f65SIngo Molnar 	 * update of the high resolution parts.
1129f24c540SThomas Gleixner 	 */
113c7a18100SThomas Gleixner 	if (clock_mode != VDSO_CLOCKMODE_NONE)
11444f57d78SVincenzo Frascino 		update_vdso_data(vdata, tk);
11544f57d78SVincenzo Frascino 
11644f57d78SVincenzo Frascino 	__arch_update_vsyscall(vdata, tk);
11744f57d78SVincenzo Frascino 
11844f57d78SVincenzo Frascino 	vdso_write_end(vdata);
11944f57d78SVincenzo Frascino 
12044f57d78SVincenzo Frascino 	__arch_sync_vdso_data(vdata);
12144f57d78SVincenzo Frascino }
12244f57d78SVincenzo Frascino 
update_vsyscall_tz(void)12344f57d78SVincenzo Frascino void update_vsyscall_tz(void)
12444f57d78SVincenzo Frascino {
12544f57d78SVincenzo Frascino 	struct vdso_data *vdata = __arch_get_k_vdso_data();
12644f57d78SVincenzo Frascino 
12744f57d78SVincenzo Frascino 	vdata[CS_HRES_COARSE].tz_minuteswest = sys_tz.tz_minuteswest;
12844f57d78SVincenzo Frascino 	vdata[CS_HRES_COARSE].tz_dsttime = sys_tz.tz_dsttime;
12944f57d78SVincenzo Frascino 
13044f57d78SVincenzo Frascino 	__arch_sync_vdso_data(vdata);
13144f57d78SVincenzo Frascino }
13219d0070aSThomas Gleixner 
13319d0070aSThomas Gleixner /**
13419d0070aSThomas Gleixner  * vdso_update_begin - Start of a VDSO update section
13519d0070aSThomas Gleixner  *
13619d0070aSThomas Gleixner  * Allows architecture code to safely update the architecture specific VDSO
13719d0070aSThomas Gleixner  * data. Disables interrupts, acquires timekeeper lock to serialize against
13819d0070aSThomas Gleixner  * concurrent updates from timekeeping and invalidates the VDSO data
13919d0070aSThomas Gleixner  * sequence counter to prevent concurrent readers from accessing
14019d0070aSThomas Gleixner  * inconsistent data.
14119d0070aSThomas Gleixner  *
14219d0070aSThomas Gleixner  * Returns: Saved interrupt flags which need to be handed in to
14319d0070aSThomas Gleixner  * vdso_update_end().
14419d0070aSThomas Gleixner  */
vdso_update_begin(void)14519d0070aSThomas Gleixner unsigned long vdso_update_begin(void)
14619d0070aSThomas Gleixner {
14719d0070aSThomas Gleixner 	struct vdso_data *vdata = __arch_get_k_vdso_data();
14819d0070aSThomas Gleixner 	unsigned long flags;
14919d0070aSThomas Gleixner 
15019d0070aSThomas Gleixner 	raw_spin_lock_irqsave(&timekeeper_lock, flags);
15119d0070aSThomas Gleixner 	vdso_write_begin(vdata);
15219d0070aSThomas Gleixner 	return flags;
15319d0070aSThomas Gleixner }
15419d0070aSThomas Gleixner 
15519d0070aSThomas Gleixner /**
15619d0070aSThomas Gleixner  * vdso_update_end - End of a VDSO update section
15719d0070aSThomas Gleixner  * @flags:	Interrupt flags as returned from vdso_update_begin()
15819d0070aSThomas Gleixner  *
15919d0070aSThomas Gleixner  * Pairs with vdso_update_begin(). Marks vdso data consistent, invokes data
16019d0070aSThomas Gleixner  * synchronization if the architecture requires it, drops timekeeper lock
16119d0070aSThomas Gleixner  * and restores interrupt flags.
16219d0070aSThomas Gleixner  */
vdso_update_end(unsigned long flags)16319d0070aSThomas Gleixner void vdso_update_end(unsigned long flags)
16419d0070aSThomas Gleixner {
16519d0070aSThomas Gleixner 	struct vdso_data *vdata = __arch_get_k_vdso_data();
16619d0070aSThomas Gleixner 
16719d0070aSThomas Gleixner 	vdso_write_end(vdata);
16819d0070aSThomas Gleixner 	__arch_sync_vdso_data(vdata);
16919d0070aSThomas Gleixner 	raw_spin_unlock_irqrestore(&timekeeper_lock, flags);
17019d0070aSThomas Gleixner }
171