mirror of
https://github.com/torvalds/linux.git
synced 2026-10-08 11:36:02 +02:00
Currently, the phase offset of time_offset and time_adjust is delivered
by adjusting tick_length in second_overflow(), and immediately draining
time_offset/time_adjust by the amount that the tick_length adjustment is
*estimated* to cause. This is fairly approximate, in part because it is
not always correct to assume that precisely NTP_INTERVAL_FREQ ticks will
occur between one call to second_overflow() and the next. It could also
over and under-run in the final second of delivery.
Instead of inflating tick_length, transfer the intended skew directly
into ntp_error each tick to achieve the desired rate.
In second_overflow(), calculate skew_delta which is the per-tick slew
rate, in the same units as time_offset: (ns << NTP_SCALE_SHIFT) / HZ.
In logarithmic_accumulation(), drain up to 'skew_delta' time units from
time_offset into ntp_error to drive the overall effective rate. The new
ntp_drain_skew() function returns the amount which is actually 'claimed'
by time_offset (and in a future patch, time_adjust). Any overrun which
is delivered by the changed 'mult' (as described below) but not claimed
by ntp_drain_skew() will remain in ntp_error to be corrected away in
subsequent ticks.
Simply transferring the precise amount from time_offset to ntp_error
would be sufficent to make the time *eventually* converge, however the
skew delivered is limited by the choice of { mult, mult+1 } each tick
and thus the convergence would be extremely slow.
In theory we could inflate ntp_err_mult with the magnitude of ntp_error
in the general case — but that would cause overcorrection in a tickless
kernel. Instead, in timekeeping_adjust(), take skew_delta into account
when calculating 'mult', such that the available {mult, mult+1} choices
bracket the overall effective rate *including* the skew, to avoid the
delta just building up in ntp_error.
The effect is that the inflated 'mult' causes ntp_error to grow because
xtime_interval is (e.g.) longer than the true tick_length. But then the
same delta is removed again as it's drained from time_offset.
This gives behaviour equivalent to the old tick_length += delta approach
but with exact per-tick accounting of the time_offset actually imparted
to the clock, and no overrun.
Signed-off-by: David Woodhouse <dwmw@amazon.co.uk>
Signed-off-by: Thomas Gleixner <tglx@kernel.org>
Assisted-by: Kiro:claude-opus-4.8
Link: https://patch.msgid.link/20260621220051.1030462-5-dwmw2@infradead.org
24 lines
1021 B
C
24 lines
1021 B
C
/* SPDX-License-Identifier: GPL-2.0 */
|
|
#ifndef _LINUX_NTP_INTERNAL_H
|
|
#define _LINUX_NTP_INTERNAL_H
|
|
|
|
extern void ntp_init(void);
|
|
extern void ntp_clear(unsigned int tkid, s64 cs_tick_adj);
|
|
/* Returns how long ticks are at present, in ns / 2^NTP_SCALE_SHIFT. */
|
|
extern u64 ntp_tick_length(unsigned int tkid);
|
|
extern s64 ntp_get_skew_delta(unsigned int tkid);
|
|
extern s64 ntp_drain_skew(unsigned int tkid, s64 amount, unsigned int shift);
|
|
extern ktime_t ntp_get_next_leap(unsigned int tkid);
|
|
extern int second_overflow(unsigned int tkid, time64_t secs);
|
|
extern int ntp_adjtimex(unsigned int tkid, struct __kernel_timex *txc, const struct timespec64 *ts,
|
|
s32 *time_tai, struct audit_ntp_data *ad);
|
|
extern void __hardpps(const struct timespec64 *phase_ts, const struct timespec64 *raw_ts);
|
|
|
|
#if defined(CONFIG_GENERIC_CMOS_UPDATE) || defined(CONFIG_RTC_SYSTOHC)
|
|
extern void ntp_notify_cmos_timer(bool offset_set);
|
|
#else
|
|
static inline void ntp_notify_cmos_timer(bool offset_set) { }
|
|
#endif
|
|
|
|
#endif /* _LINUX_NTP_INTERNAL_H */
|