From d375af58990902e73dd62bd7c049e759bcff92a5 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Sun, 21 Jun 2026 22:53:57 +0100 Subject: timekeeping: Drive time_offset skew via per-tick ntp_error transfer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Signed-off-by: Thomas Gleixner Assisted-by: Kiro:claude-opus-4.8 Link: https://patch.msgid.link/20260621220051.1030462-5-dwmw2@infradead.org --- include/linux/timekeeper_internal.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include/linux') diff --git a/include/linux/timekeeper_internal.h b/include/linux/timekeeper_internal.h index 9c53f44537f0..9c198f65621b 100644 --- a/include/linux/timekeeper_internal.h +++ b/include/linux/timekeeper_internal.h @@ -189,6 +189,7 @@ struct timekeeper { u32 ntp_err_mult; s64 cs_tick_adj; u32 skip_second_overflow; + s64 skew_delta; s32 tai_offset; }; -- cgit v1.2.3