summaryrefslogtreecommitdiff
path: root/include/linux
diff options
context:
space:
mode:
authorDavid Woodhouse <dwmw@amazon.co.uk>2026-06-21 22:53:56 +0100
committerThomas Gleixner <tglx@kernel.org>2026-07-10 09:20:54 +0200
commit869a55e662a080a5c6618b68ae320231c720eeee (patch)
treec5d6f55d7db45ffe6862c3ee66ace199229898d3 /include/linux
parentb7befd6d91207cf3f4cecd68fea0c212093906cf (diff)
timekeeping: Account for clocksource tick quantisation via NTP
cycle_interval is an integer number of counter cycles per NTP interval, so the real time it represents differs from the nominal NTP_INTERVAL_LENGTH by up to half a counter period. For coarse clocksources this is significant: the 3.579545 MHz ACPI PM timer at HZ=1000 rounds 3579.545 cycles up to 3580, making each tick 1.000127 ms (+127 PPM). Commit a386b5af8edd ("time: Compensate for rounding on odd-frequency clocksources") introduced xtime_remainder to compensate for exactly this, citing the same 127 PPM ACPI PM example. The compensation is correct and necessary, but it was applied inside the timekeeping accumulation in timekeeping.c: subtracted in the mult computation in timekeeping_adjust() and folded into the ntp_error update in logarithmic_accumulation(). That keeps the base rate correct and leaves NTP its full symmetric +/-MAXFREQ range rather than +373/-627 PPM, but the NTP code in ntp.c never sees it: tick_length is computed without the correction, so ntp.c's notion of how long a tick is disagrees with the rate timekeeping actually produces. Make the offset an explicit part of the NTP tick_length instead. Add ntp_data::cs_tick_adj, a fixed per-second addend that ntp_update_frequency() includes alongside ntp_tick_adj and time_freq. tk_setup_internals() computes it from the difference between the real cycle_interval duration and the nominal interval, stores it in the timekeeper, and hands it to NTP through a new argument to ntp_clear() -- which already recomputes the frequency and is invoked after every clocksource (re)configuration. timekeeping_init() now uses TK_UPDATE_ALL for this; clearing NTP there is otherwise redundant since ntp_init() has just initialised it. ntp.c now computes the true tick rate, giving a single source of truth. Like ntp_tick_adj, cs_tick_adj stays internal to the kernel: userspace still sees the nominal 1.000000 ms tick via adjtimex and is unaware of the addends. timekeeping_adjust() and logarithmic_accumulation() use ntp_tick / xtime_interval directly, and xtime_remainder is removed. The base-rate arithmetic is unchanged: ntp_tick becomes xtime_interval << ntp_error_shift, so the mult division yields the same base mult and the ntp_error accumulation still nets to zero per tick. Beyond the cleanup of treating all the tick_length contributions (nominal interval, ntp_tick_adj, cs_tick_adj, time_freq) consistently as addends in one place, it also prepares for feed-forward discipline: a future timekeeping_set_reference() will set tick_length to track an absolute external reference such as a vmclock, and that path needs ntp.c to own a tick_length that already reflects the clocksource quantisation, with no hidden correction applied elsewhere. Signed-off-by: David Woodhouse <dwmw@amazon.co.uk> Signed-off-by: Thomas Gleixner <tglx@kernel.org> Assisted-by: Kiro:claude-opus-4.8 Acked-by: John Stultz <jstultz@google.com> Link: https://patch.msgid.link/20260621220051.1030462-4-dwmw2@infradead.org
Diffstat (limited to 'include/linux')
-rw-r--r--include/linux/timekeeper_internal.h8
1 files changed, 5 insertions, 3 deletions
diff --git a/include/linux/timekeeper_internal.h b/include/linux/timekeeper_internal.h
index 264db7c9c071..9c53f44537f0 100644
--- a/include/linux/timekeeper_internal.h
+++ b/include/linux/timekeeper_internal.h
@@ -84,8 +84,6 @@ struct tk_read_base {
* @cycle_interval: Number of clock cycles in one NTP interval
* @xtime_interval: Number of clock shifted nano seconds in one NTP
* interval.
- * @xtime_remainder: Shifted nano seconds left over when rounding
- * @cycle_interval
* @raw_interval: Shifted raw nano seconds accumulated per NTP interval.
* @next_leap_ktime: CLOCK_MONOTONIC time value of a pending leap-second
* @ntp_tick: The ntp_tick_length() value currently being
@@ -99,6 +97,10 @@ struct tk_read_base {
* @ntp_error_shift: Shift conversion between clock shifted nano seconds and
* ntp shifted nano seconds.
* @ntp_err_mult: Multiplication factor for scaled math conversion
+ * @cs_tick_adj: Per-second adjustment handed to NTP via ntp_clear()
+ * accounting for the difference between the nominal
+ * NTP interval and the real time taken by the
+ * clocksource's integer @cycle_interval (upscaled).
* @skip_second_overflow: Flag used to avoid updating NTP twice with same second
* @tai_offset: The current UTC to TAI offset in seconds
*
@@ -178,7 +180,6 @@ struct timekeeper {
u64 cycle_interval;
u64 xtime_interval;
- s64 xtime_remainder;
u64 raw_interval;
ktime_t next_leap_ktime;
@@ -186,6 +187,7 @@ struct timekeeper {
s64 ntp_error;
u32 ntp_error_shift;
u32 ntp_err_mult;
+ s64 cs_tick_adj;
u32 skip_second_overflow;
s32 tai_offset;
};