summaryrefslogtreecommitdiff
path: root/kernel/time
diff options
context:
space:
mode:
authorDavid Woodhouse <dwmw@amazon.co.uk>2026-06-21 22:53:55 +0100
committerThomas Gleixner <tglx@kernel.org>2026-07-08 00:17:26 +0200
commitb7befd6d91207cf3f4cecd68fea0c212093906cf (patch)
treea97b2743db93ff7f728e2e57bb24db051946f183 /kernel/time
parent79b8bd857bd7f5a8c970fc50c611062b96fe56e5 (diff)
timekeeping: Account for monotonicity adjustment in ntp_error
timekeeping_apply_adjustment() modifies xtime_nsec to ensure monotonicity when mult changes: xtime_nsec -= offset This ensures that the time reported to userspace does not jump when the multiplier is adjusted from one tick to the next. However, the ntp_error accumulator which tracks the difference between intended and actual clock position was not being updated to reflect this additional discrepancy. An earlier attempt at this compensation existed as: ntp_error -= (interval - offset) << ntp_error_shift but was removed in commit c2cda2a5bda9 ("timekeeping/ntp: Don't align NTP frequency adjustments to ticks") because it was a major source of NTP error. That's because (interval - offset) was wrong: the subtraction of "interval" prematurely accounted for the changed xtime_interval of the next tick, which would be correctly accounted in the next accumulation anyway — a double subtraction. What is actually needed is just the "offset" part: ntp_error must be told that xtime_nsec moved by "offset" without a corresponding change in the intended position. For the normal ±1 mult dithering this is negligible (the adjustments cancel over time), but for larger mult changes — such as when an external reference clock sets a new frequency — the one-time uncompensated offset is significant. Fix by adjusting ntp_error by the correct amount: ntp_error += offset << ntp_error_shift This keeps ntp_error consistent with the actual xtime_nsec position after the adjustment, and ensures the discrepancy is correctly smoothed away over time and the clock returns to where it should have been. Fixes: c2cda2a5bda9 ("timekeeping/ntp: Don't align NTP frequency adjustments to ticks") Signed-off-by: David Woodhouse <dwmw@amazon.co.uk> Signed-off-by: Thomas Gleixner <tglx@kernel.org> Assisted-by: Kiro:claude-opus-4.6-1m Acked-by: John Stultz <jstultz@google.com> Link: https://patch.msgid.link/20260621220051.1030462-3-dwmw2@infradead.org
Diffstat (limited to 'kernel/time')
-rw-r--r--kernel/time/timekeeping.c6
1 files changed, 6 insertions, 0 deletions
diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c
index 2527a483d75c..9e1cbc590988 100644
--- a/kernel/time/timekeeping.c
+++ b/kernel/time/timekeeping.c
@@ -2404,6 +2404,11 @@ static __always_inline void timekeeping_apply_adjustment(struct timekeeper *tk,
* xtime_nsec_2 = xtime_nsec_1 - offset
* Which simplifies to:
* xtime_nsec -= offset
+ *
+ * When subtracting offset from xtime_nsec, the same amount
+ * (in appropriate units) has to be added to ntp_error, in
+ * order to correctly track the delta between the time
+ * reported in xtime_nsec, and the intended time.
*/
if ((mult_adj > 0) && (tk->tkr_mono.mult + mult_adj < mult_adj)) {
/* NTP adjustment caused clocksource mult overflow */
@@ -2414,6 +2419,7 @@ static __always_inline void timekeeping_apply_adjustment(struct timekeeper *tk,
tk->tkr_mono.mult += mult_adj;
tk->xtime_interval += interval;
tk->tkr_mono.xtime_nsec -= offset;
+ tk->ntp_error += offset << tk->ntp_error_shift;
}
/*