Re: [RFC] Enabling CONFIG_NTP_PPS for NOHZ by adding ntp_error to system_time_snapshot
From: Thomas Gleixner
Date: Fri Jun 19 2026 - 09:35:23 EST
On Fri, Jun 19 2026 at 01:33, David Woodhouse wrote:
> @@ -1285,6 +1286,45 @@ void ktime_get_snapshot_id(clockid_t clock_id, struct system_time_snapshot *syst
>
> nsec_sys = timekeeping_cycles_to_ns(&tk->tkr_mono, now);
> nsec_raw = timekeeping_cycles_to_ns(&tk->tkr_raw, now);
> +
> + /*
> + * For the NTP-disciplined mono-based clocks, report how far
> + * @systime is from the ideal NTP time at @now, in signed ns,
> + * so a caller can land on the ideal line by adding it. Four
> + * terms, summed in ns << NTP_SCALE_SHIFT before converting:
> + *
> + * - tk->ntp_error, the deviation as of the last update;
> + * - (cycle_delta * ntp_err_frac), the fractional-mult drift
> + * accrued since then (cycle_delta is at most a tick on a
> + * tickful kernel, but many ticks' worth under NO_HZ);
> + * - (cycle_delta * ntp_err_mult), subtracting the applied +1
> + * mult dither over the same span;
> + * - the sub-ns fraction @systime dropped when the read was
> + * truncated to whole ns (low @shift bits, exact despite the
> + * multiply overflowing).
> + *
> + * RAW is undisciplined and AUX has its own discipline, so they
> + * carry no ntp_error.
AUX has ntp_error too. AUX clocks have a per clock NTP instance, which
work exactly like the main timerkeeper's one. Only CLOCK_MONOTONIC_RAW
needs to be excluded.
> + */
> + if (clock_id == CLOCK_REALTIME || clock_id == CLOCK_MONOTONIC ||
> + clock_id == CLOCK_BOOTTIME) {
> + u32 nes = tk->ntp_error_shift;
> + u64 cycle_delta = (now - tk->tkr_mono.cycle_last) &
> + tk->tkr_mono.mask;
> + s64 err = tk->ntp_error +
> + (((s64)mul_u64_u64_shr(cycle_delta,
> + tk->ntp_err_frac, 32) -
> + (s64)(cycle_delta * tk->ntp_err_mult)) << nes);
> +
> + err += (s64)((cycle_delta * tk->tkr_mono.mult +
> + tk->tkr_mono.xtime_nsec) &
> + ((1ULL << tk->tkr_mono.shift) - 1)) << nes;
> + systime_snapshot->ntp_error =
> + (err + (1LL << (NTP_SCALE_SHIFT - 1))) >>
> + NTP_SCALE_SHIFT;
This formatting makes my brain hurt. Can you please split that out into
a separate function?
/*
* Big fat comment....
*/
static void snapshot_ntp_error(clockid_t clock_id, struct system_time_snapshot *snap,
struct timekeeper *tk)
{
if (clock_id == CLOCK_MONOTONIC_RAW) {
snap->ntp_error = 0;
return;
}
u64 cycle_delta = (now - tk->tkr_mono.cycle_last) & tk->tkr_mono.mask;
u32 nes = tk->ntp_error_shift;
s64 tmp, err = tk->ntp_error;
err += ((s64)mul_u64_u64_shr(cycle_delta, tk->ntp_err_frac, 32) -
(s64)(cycle_delta * tk->ntp_err_mult)) << nes;
tmp = (s64)(cycle_delta * tk->tkr_mono.mult + tk->tkr_mono.xtime_nsec);
tmp &= (1ULL << tk->tkr_mono.shift) - 1;
err += tmp << nes;
snap->ntp_error = (err + (1LL << (NTP_SCALE_SHIFT - 1))) >> NTP_SCALE_SHIFT;
}
or something readable like that.