mirror of
https://github.com/torvalds/linux.git
synced 2026-04-18 14:53:58 -04:00
Some architectures have clockevent devices which are coupled to the system
clocksource by implementing a less than or equal comparator which compares
the programmed absolute expiry time against the underlying time
counter. Well known examples are TSC/TSC deadline timer and the S390 TOD
clocksource/comparator.
While the concept is nice it has some downsides:
1) The clockevents core code is strictly based on relative expiry times
as that's the most common case for clockevent device hardware. That
requires to convert the absolute expiry time provided by the caller
(hrtimers, NOHZ code) to a relative expiry time by reading and
substracting the current time.
The clockevent::set_next_event() callback must then read the counter
again to convert the relative expiry back into a absolute one.
2) The conversion factors from nanoseconds to counter clock cycles are
set up when the clockevent is registered. When NTP applies corrections
then the clockevent conversion factors can deviate from the
clocksource conversion substantially which either results in timers
firing late or in the worst case early. The early expiry then needs to
do a reprogam with a short delta.
In most cases this is papered over by the fact that the read in the
set_next_event() callback happens after the read which is used to
calculate the delta. So the tendency is that timers expire mostly
late.
All of this can be avoided by providing support for these devices in the
core code:
1) The timekeeping core keeps track of the last update to the clocksource
by storing the base nanoseconds and the corresponding clocksource
counter value. That's used to keep the conversion math for reading the
time within 64-bit in the common case.
This information can be used to avoid both reads of the underlying
clocksource in the clockevents reprogramming path:
delta = expiry - base_ns;
cycles = base_cycles + ((delta * clockevent::mult) >> clockevent::shift);
The resulting cycles value can be directly used to program the
comparator.
2) As #1 does not longer provide the "compensation" through the second
read the deviation of the clocksource and clockevent conversions
caused by NTP become more prominent.
This can be cured by letting the timekeeping core compute and store
the reverse conversion factors when the clocksource cycles to
nanoseconds factors are modified by NTP:
CS::MULT (1 << NS_TO_CYC_SHIFT)
--------------- = ----------------------
(1 << CS:SHIFT) NS_TO_CYC_MULT
Ergo: NS_TO_CYC_MULT = (1 << (CS::SHIFT + NS_TO_CYC_SHIFT)) / CS::MULT
The NS_TO_CYC_SHIFT value is calculated when the clocksource is
installed so that it aims for a one hour maximum sleep time.
Signed-off-by: Thomas Gleixner <tglx@kernel.org>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Link: https://patch.msgid.link/20260224163429.944763521@kernel.org
37 lines
1.0 KiB
C
37 lines
1.0 KiB
C
/* SPDX-License-Identifier: GPL-2.0 */
|
|
#ifndef _KERNEL_TIME_TIMEKEEPING_H
|
|
#define _KERNEL_TIME_TIMEKEEPING_H
|
|
/*
|
|
* Internal interfaces for kernel/time/
|
|
*/
|
|
extern ktime_t ktime_get_update_offsets_now(unsigned int *cwsseq,
|
|
ktime_t *offs_real,
|
|
ktime_t *offs_boot,
|
|
ktime_t *offs_tai);
|
|
|
|
bool ktime_expiry_to_cycles(enum clocksource_ids id, ktime_t expires_ns, u64 *cycles);
|
|
|
|
extern int timekeeping_valid_for_hres(void);
|
|
extern u64 timekeeping_max_deferment(void);
|
|
extern void timekeeping_warp_clock(void);
|
|
extern int timekeeping_suspend(void);
|
|
extern void timekeeping_resume(void);
|
|
#ifdef CONFIG_GENERIC_SCHED_CLOCK
|
|
extern int sched_clock_suspend(void);
|
|
extern void sched_clock_resume(void);
|
|
#else
|
|
static inline int sched_clock_suspend(void) { return 0; }
|
|
static inline void sched_clock_resume(void) { }
|
|
#endif
|
|
|
|
extern void update_process_times(int user);
|
|
extern void do_timer(unsigned long ticks);
|
|
extern void update_wall_time(void);
|
|
|
|
extern raw_spinlock_t jiffies_lock;
|
|
extern seqcount_raw_spinlock_t jiffies_seq;
|
|
|
|
#define CS_NAME_LEN 32
|
|
|
|
#endif
|