1 // SPDX-License-Identifier: GPL-2.0
3 * Generic userspace implementations of gettimeofday() and similar.
5 #include <vdso/datapage.h>
6 #include <vdso/helpers.h>
10 #ifdef VDSO_DELTA_NOMASK
11 # define VDSO_DELTA_MASK(vd) ULLONG_MAX
13 # define VDSO_DELTA_MASK(vd) (vd->mask)
16 #ifdef CONFIG_GENERIC_VDSO_OVERFLOW_PROTECT
17 static __always_inline
bool vdso_delta_ok(const struct vdso_data
*vd
, u64 delta
)
19 return delta
< vd
->max_cycles
;
22 static __always_inline
bool vdso_delta_ok(const struct vdso_data
*vd
, u64 delta
)
29 static __always_inline u64
vdso_shift_ns(u64 ns
, u32 shift
)
36 * Default implementation which works for all sane clocksources. That
37 * obviously excludes x86/TSC.
39 static __always_inline u64
vdso_calc_ns(const struct vdso_data
*vd
, u64 cycles
, u64 base
)
41 u64 delta
= (cycles
- vd
->cycle_last
) & VDSO_DELTA_MASK(vd
);
43 if (likely(vdso_delta_ok(vd
, delta
)))
44 return vdso_shift_ns((delta
* vd
->mult
) + base
, vd
->shift
);
46 return mul_u64_u32_add_u64_shr(delta
, vd
->mult
, base
, vd
->shift
);
48 #endif /* vdso_calc_ns */
50 #ifndef __arch_vdso_hres_capable
51 static inline bool __arch_vdso_hres_capable(void)
57 #ifndef vdso_clocksource_ok
58 static inline bool vdso_clocksource_ok(const struct vdso_data
*vd
)
60 return vd
->clock_mode
!= VDSO_CLOCKMODE_NONE
;
64 #ifndef vdso_cycles_ok
65 static inline bool vdso_cycles_ok(u64 cycles
)
72 static __always_inline
int do_hres_timens(const struct vdso_data
*vdns
, clockid_t clk
,
73 struct __kernel_timespec
*ts
)
75 const struct timens_offset
*offs
= &vdns
->offset
[clk
];
76 const struct vdso_timestamp
*vdso_ts
;
77 const struct vdso_data
*vd
;
82 vd
= vdns
- (clk
== CLOCK_MONOTONIC_RAW
? CS_RAW
: CS_HRES_COARSE
);
83 vd
= __arch_get_timens_vdso_data(vd
);
84 if (clk
!= CLOCK_MONOTONIC_RAW
)
85 vd
= &vd
[CS_HRES_COARSE
];
88 vdso_ts
= &vd
->basetime
[clk
];
91 seq
= vdso_read_begin(vd
);
93 if (unlikely(!vdso_clocksource_ok(vd
)))
96 cycles
= __arch_get_hw_counter(vd
->clock_mode
, vd
);
97 if (unlikely(!vdso_cycles_ok(cycles
)))
99 ns
= vdso_calc_ns(vd
, cycles
, vdso_ts
->nsec
);
101 } while (unlikely(vdso_read_retry(vd
, seq
)));
103 /* Add the namespace offset */
108 * Do this outside the loop: a race inside the loop could result
109 * in __iter_div_u64_rem() being extremely slow.
111 ts
->tv_sec
= sec
+ __iter_div_u64_rem(ns
, NSEC_PER_SEC
, &ns
);
117 static __always_inline
118 const struct vdso_data
*__arch_get_timens_vdso_data(const struct vdso_data
*vd
)
123 static __always_inline
int do_hres_timens(const struct vdso_data
*vdns
, clockid_t clk
,
124 struct __kernel_timespec
*ts
)
130 static __always_inline
int do_hres(const struct vdso_data
*vd
, clockid_t clk
,
131 struct __kernel_timespec
*ts
)
133 const struct vdso_timestamp
*vdso_ts
= &vd
->basetime
[clk
];
137 /* Allows to compile the high resolution parts out */
138 if (!__arch_vdso_hres_capable())
143 * Open coded function vdso_read_begin() to handle
144 * VDSO_CLOCKMODE_TIMENS. Time namespace enabled tasks have a
145 * special VVAR page installed which has vd->seq set to 1 and
146 * vd->clock_mode set to VDSO_CLOCKMODE_TIMENS. For non time
147 * namespace affected tasks this does not affect performance
148 * because if vd->seq is odd, i.e. a concurrent update is in
149 * progress the extra check for vd->clock_mode is just a few
150 * extra instructions while spin waiting for vd->seq to become
153 while (unlikely((seq
= READ_ONCE(vd
->seq
)) & 1)) {
154 if (IS_ENABLED(CONFIG_TIME_NS
) &&
155 vd
->clock_mode
== VDSO_CLOCKMODE_TIMENS
)
156 return do_hres_timens(vd
, clk
, ts
);
161 if (unlikely(!vdso_clocksource_ok(vd
)))
164 cycles
= __arch_get_hw_counter(vd
->clock_mode
, vd
);
165 if (unlikely(!vdso_cycles_ok(cycles
)))
167 ns
= vdso_calc_ns(vd
, cycles
, vdso_ts
->nsec
);
169 } while (unlikely(vdso_read_retry(vd
, seq
)));
172 * Do this outside the loop: a race inside the loop could result
173 * in __iter_div_u64_rem() being extremely slow.
175 ts
->tv_sec
= sec
+ __iter_div_u64_rem(ns
, NSEC_PER_SEC
, &ns
);
181 #ifdef CONFIG_TIME_NS
182 static __always_inline
int do_coarse_timens(const struct vdso_data
*vdns
, clockid_t clk
,
183 struct __kernel_timespec
*ts
)
185 const struct vdso_data
*vd
= __arch_get_timens_vdso_data(vdns
);
186 const struct vdso_timestamp
*vdso_ts
= &vd
->basetime
[clk
];
187 const struct timens_offset
*offs
= &vdns
->offset
[clk
];
193 seq
= vdso_read_begin(vd
);
195 nsec
= vdso_ts
->nsec
;
196 } while (unlikely(vdso_read_retry(vd
, seq
)));
198 /* Add the namespace offset */
203 * Do this outside the loop: a race inside the loop could result
204 * in __iter_div_u64_rem() being extremely slow.
206 ts
->tv_sec
= sec
+ __iter_div_u64_rem(nsec
, NSEC_PER_SEC
, &nsec
);
211 static __always_inline
int do_coarse_timens(const struct vdso_data
*vdns
, clockid_t clk
,
212 struct __kernel_timespec
*ts
)
218 static __always_inline
int do_coarse(const struct vdso_data
*vd
, clockid_t clk
,
219 struct __kernel_timespec
*ts
)
221 const struct vdso_timestamp
*vdso_ts
= &vd
->basetime
[clk
];
226 * Open coded function vdso_read_begin() to handle
227 * VDSO_CLOCK_TIMENS. See comment in do_hres().
229 while ((seq
= READ_ONCE(vd
->seq
)) & 1) {
230 if (IS_ENABLED(CONFIG_TIME_NS
) &&
231 vd
->clock_mode
== VDSO_CLOCKMODE_TIMENS
)
232 return do_coarse_timens(vd
, clk
, ts
);
237 ts
->tv_sec
= vdso_ts
->sec
;
238 ts
->tv_nsec
= vdso_ts
->nsec
;
239 } while (unlikely(vdso_read_retry(vd
, seq
)));
244 static __always_inline
int
245 __cvdso_clock_gettime_common(const struct vdso_data
*vd
, clockid_t clock
,
246 struct __kernel_timespec
*ts
)
250 /* Check for negative values or invalid clocks */
251 if (unlikely((u32
) clock
>= MAX_CLOCKS
))
255 * Convert the clockid to a bitmask and use it to check which
256 * clocks are handled in the VDSO directly.
259 if (likely(msk
& VDSO_HRES
))
260 vd
= &vd
[CS_HRES_COARSE
];
261 else if (msk
& VDSO_COARSE
)
262 return do_coarse(&vd
[CS_HRES_COARSE
], clock
, ts
);
263 else if (msk
& VDSO_RAW
)
268 return do_hres(vd
, clock
, ts
);
271 static __maybe_unused
int
272 __cvdso_clock_gettime_data(const struct vdso_data
*vd
, clockid_t clock
,
273 struct __kernel_timespec
*ts
)
275 int ret
= __cvdso_clock_gettime_common(vd
, clock
, ts
);
278 return clock_gettime_fallback(clock
, ts
);
282 static __maybe_unused
int
283 __cvdso_clock_gettime(clockid_t clock
, struct __kernel_timespec
*ts
)
285 return __cvdso_clock_gettime_data(__arch_get_vdso_data(), clock
, ts
);
289 static __maybe_unused
int
290 __cvdso_clock_gettime32_data(const struct vdso_data
*vd
, clockid_t clock
,
291 struct old_timespec32
*res
)
293 struct __kernel_timespec ts
;
296 ret
= __cvdso_clock_gettime_common(vd
, clock
, &ts
);
299 return clock_gettime32_fallback(clock
, res
);
302 res
->tv_sec
= ts
.tv_sec
;
303 res
->tv_nsec
= ts
.tv_nsec
;
308 static __maybe_unused
int
309 __cvdso_clock_gettime32(clockid_t clock
, struct old_timespec32
*res
)
311 return __cvdso_clock_gettime32_data(__arch_get_vdso_data(), clock
, res
);
313 #endif /* BUILD_VDSO32 */
315 static __maybe_unused
int
316 __cvdso_gettimeofday_data(const struct vdso_data
*vd
,
317 struct __kernel_old_timeval
*tv
, struct timezone
*tz
)
320 if (likely(tv
!= NULL
)) {
321 struct __kernel_timespec ts
;
323 if (do_hres(&vd
[CS_HRES_COARSE
], CLOCK_REALTIME
, &ts
))
324 return gettimeofday_fallback(tv
, tz
);
326 tv
->tv_sec
= ts
.tv_sec
;
327 tv
->tv_usec
= (u32
)ts
.tv_nsec
/ NSEC_PER_USEC
;
330 if (unlikely(tz
!= NULL
)) {
331 if (IS_ENABLED(CONFIG_TIME_NS
) &&
332 vd
->clock_mode
== VDSO_CLOCKMODE_TIMENS
)
333 vd
= __arch_get_timens_vdso_data(vd
);
335 tz
->tz_minuteswest
= vd
[CS_HRES_COARSE
].tz_minuteswest
;
336 tz
->tz_dsttime
= vd
[CS_HRES_COARSE
].tz_dsttime
;
342 static __maybe_unused
int
343 __cvdso_gettimeofday(struct __kernel_old_timeval
*tv
, struct timezone
*tz
)
345 return __cvdso_gettimeofday_data(__arch_get_vdso_data(), tv
, tz
);
349 static __maybe_unused __kernel_old_time_t
350 __cvdso_time_data(const struct vdso_data
*vd
, __kernel_old_time_t
*time
)
352 __kernel_old_time_t t
;
354 if (IS_ENABLED(CONFIG_TIME_NS
) &&
355 vd
->clock_mode
== VDSO_CLOCKMODE_TIMENS
)
356 vd
= __arch_get_timens_vdso_data(vd
);
358 t
= READ_ONCE(vd
[CS_HRES_COARSE
].basetime
[CLOCK_REALTIME
].sec
);
366 static __maybe_unused __kernel_old_time_t
__cvdso_time(__kernel_old_time_t
*time
)
368 return __cvdso_time_data(__arch_get_vdso_data(), time
);
370 #endif /* VDSO_HAS_TIME */
372 #ifdef VDSO_HAS_CLOCK_GETRES
373 static __maybe_unused
374 int __cvdso_clock_getres_common(const struct vdso_data
*vd
, clockid_t clock
,
375 struct __kernel_timespec
*res
)
380 /* Check for negative values or invalid clocks */
381 if (unlikely((u32
) clock
>= MAX_CLOCKS
))
384 if (IS_ENABLED(CONFIG_TIME_NS
) &&
385 vd
->clock_mode
== VDSO_CLOCKMODE_TIMENS
)
386 vd
= __arch_get_timens_vdso_data(vd
);
389 * Convert the clockid to a bitmask and use it to check which
390 * clocks are handled in the VDSO directly.
393 if (msk
& (VDSO_HRES
| VDSO_RAW
)) {
395 * Preserves the behaviour of posix_get_hrtimer_res().
397 ns
= READ_ONCE(vd
[CS_HRES_COARSE
].hrtimer_res
);
398 } else if (msk
& VDSO_COARSE
) {
400 * Preserves the behaviour of posix_get_coarse_res().
414 static __maybe_unused
415 int __cvdso_clock_getres_data(const struct vdso_data
*vd
, clockid_t clock
,
416 struct __kernel_timespec
*res
)
418 int ret
= __cvdso_clock_getres_common(vd
, clock
, res
);
421 return clock_getres_fallback(clock
, res
);
425 static __maybe_unused
426 int __cvdso_clock_getres(clockid_t clock
, struct __kernel_timespec
*res
)
428 return __cvdso_clock_getres_data(__arch_get_vdso_data(), clock
, res
);
432 static __maybe_unused
int
433 __cvdso_clock_getres_time32_data(const struct vdso_data
*vd
, clockid_t clock
,
434 struct old_timespec32
*res
)
436 struct __kernel_timespec ts
;
439 ret
= __cvdso_clock_getres_common(vd
, clock
, &ts
);
442 return clock_getres32_fallback(clock
, res
);
445 res
->tv_sec
= ts
.tv_sec
;
446 res
->tv_nsec
= ts
.tv_nsec
;
451 static __maybe_unused
int
452 __cvdso_clock_getres_time32(clockid_t clock
, struct old_timespec32
*res
)
454 return __cvdso_clock_getres_time32_data(__arch_get_vdso_data(),
457 #endif /* BUILD_VDSO32 */
458 #endif /* VDSO_HAS_CLOCK_GETRES */