[RFC PATCH v2 3/8] timekeeping: Clamp time_offset delta to prevent infinite tail
From: David Woodhouse
Date: Sun May 17 2026 - 18:04:20 EST
From: David Woodhouse <dwmw@xxxxxxxxxxxx>
ntp_offset_chunk() computes delta as time_offset >> (SHIFT_PLL +
time_constant), which exponentially decays toward zero but never
reaches it. This means time_offset asymptotically approaches zero
without ever completing — the clock never fully converges.
Fix by clamping delta:
- Minimum: 20ns/sec (NTP_OFFSET_DELTA_MIN), ensuring the tail
converges in finite time
- Maximum: time_offset itself, preventing overshoot on the final
second
This preserves the exponential decay behavior for large offsets
while ensuring precise convergence for the tail.
Signed-off-by: David Woodhouse <dwmw@xxxxxxxxxxxx>
---
kernel/time/ntp.c | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/kernel/time/ntp.c b/kernel/time/ntp.c
index 97fa99b96dd0..2d6d00ae5bf7 100644
--- a/kernel/time/ntp.c
+++ b/kernel/time/ntp.c
@@ -104,6 +104,8 @@ static struct ntp_data tk_ntp_data[TIMEKEEPERS_MAX] = {
#define MAX_TICKADJ_SCALED \
(((MAX_TICKADJ * NSEC_PER_USEC) << NTP_SCALE_SHIFT) / NTP_INTERVAL_FREQ)
#define MAX_TAI_OFFSET 100000
+/* Minimum skew rate for exponential tail: 20ns/s in tick_length units */
+#define NTP_OFFSET_DELTA_MIN (((s64)20 << NTP_SCALE_SHIFT) / NTP_INTERVAL_FREQ)
#ifdef CONFIG_NTP_PPS
@@ -461,6 +463,17 @@ int second_overflow(unsigned int tkid, time64_t secs)
ntpdata->tick_length = ntpdata->tick_length_base;
delta = ntp_offset_chunk(ntpdata, ntpdata->time_offset);
+ if (ntpdata->time_offset > 0) {
+ if (delta < NTP_OFFSET_DELTA_MIN)
+ delta = NTP_OFFSET_DELTA_MIN;
+ if (delta > ntpdata->time_offset)
+ delta = ntpdata->time_offset;
+ } else if (ntpdata->time_offset < 0) {
+ if (delta > -NTP_OFFSET_DELTA_MIN)
+ delta = -NTP_OFFSET_DELTA_MIN;
+ if (delta < ntpdata->time_offset)
+ delta = ntpdata->time_offset;
+ }
ntpdata->time_offset -= delta;
ntpdata->tick_length += delta;
--
2.51.0