On NTP, RTCs and accurately setting their time

Russell King - ARM Linux linux at armlinux.org.uk
Wed Sep 20 06:23:48 PDT 2017


On Wed, Sep 20, 2017 at 12:21:52PM +0100, Russell King - ARM Linux wrote:
> Hi,
> 
> It's common for systems to be time synchronised using programs such as
> chrony or ntpd.  In such situations, when we are properly synchronised,
> the kernel writes the current time to the RTC every 11 seconds.

That should be 11 minutes.

> 
> However, assumptions are made about the RTC:
> 
> 1. kernel/time/ntp.c assumes that all RTCs want to be told to set the
>    time at around 500ms into the second.
> 
> 2. drivers/rtc/systohc.c assumes that if the time being set is >= 500ms,
>    then we want to set the _next_ second.
> 
> This leads to RTCs being set with an offset time, where the offset
> depends on the RTC.  For example, the PCF8523 ends up reliably set
> around 970ms in the future:
> 
> RTC Time: 19-09-2017 14:27:51
> System Time was:     14:27:50.031
> 
> What this means is that when the RTC ticked from 50 to 51 seconds,
> system time was at 50.031s.  Hence, the RTC in this case is 969ms
> in the future.
> 
> For Armada 388, the situation is different:
> 
> RTC Time: 19-09-2017 14:48:17
> System Time was:     14:48:16.521
> 
> Here, the RTC is being set 479ms in the future.
> 
> The SNVS RTC in imx6 is different again, although I have no definitive
> figures yet.
> 
> Why does this matter - if you introduce a 1s offset in system time
> (eg, you reboot a machine running NTP) then it takes at least four hours
> after the reboot for ntpd to settle down, with the PPM swinging faster
> and slower than normal as it compensates for the offset.  This means if
> you are using a Linux system for time measurement purposes, it is
> useless for those four hours.
> 
> Being able to accurately save and restore system time helps to reduce
> the disruptive effect of a reboot.
> 
> Currently, there are no controls in the kernel over this mechanism - if
> you're running a multi-platform kernel which has support for writing the
> RTC if ntp-sync'd, then you're stuck with the kernel writing the RTC
> every 11 seconds.  Not only is this detrimental due to the enforced RTC

and here too.

> dependent offset, but it also means that if you want to trim your RTC
> to a synchronised source of time (for which the kernel must not write
> the RTC, but you do want it to ntp sync), the only way to do it is to
> either modify the kernel, or disable CONFIG_RTC_SYSTOHC in the multi-
> platform kernel.
> 
> 
> The kernel can do better - elimination of the rounding-up in systohc.c
> gives a better result for PCF8523:
> 
> RTC Time: 19-09-2017 16:30:38
> System Time was:     16:30:38.034
> 
> and the remaining offset can be reduced by adjusting the 500ms offset
> in ntp.c to 470ms.  This is specific to the offset that PCF8523 wants.
> We know that MC146818 RTCs found in PCs want a 500ms offset (without
> the addition of one second that systohc.c does.)  The Armada 388 RTC
> wants to be set on the exact second.
> 
> We need some way to cater for these differing requirements (eg, RTC
> drivers provide some properties to rtclib and the ntp code to describe
> how long it takes for a written time to "take"), or we decide (as I
> think was decided in the past) that the kernel should not be setting
> the RTC, but userspace should be responsible for performing that
> function.  Either way, we need to know about these RTC specific
> properties in order to set their time accurately.
> 
> One of the issues here, however, is that RTC datasheets do not give this
> information - this can only be found out by experimentation and
> measurement.
> 
> Currently I'm using the hacky patch below to be able to (a) disable the
> regular RTC write during runtime, so I can measure the RTC drift and
> trim it, and (b) provide a knob to adjust how far past the second the
> RTC will receive its write.  For a properly trimmed PCF8523 (measured
> over about 12 hours to have 0.1ppm drift - which is better than its
> associated crystal is rated for), setting this knob to 470ms results
> in the following (from two samples this morning):
> 
> RTC Time: 20-09-2017 10:41:30
> System Time was:     10:41:30.000
> RTC Time: 20-09-2017 11:17:38
> System Time was:     11:17:38.000
> 
> There's probably some noise in the setting of this due to the workqueue,
> but getting it within 10ms is definitely an improvement over being
> almost a second out.
> 
> So, the question is... how should these differences in rtc requirements
> be handled?
> 
>  drivers/rtc/systohc.c |  6 +++---
>  kernel/sysctl.c       | 21 +++++++++++++++++++++
>  kernel/time/ntp.c     |  9 ++++++---
>  3 files changed, 30 insertions(+), 6 deletions(-)
> 
> diff --git a/drivers/rtc/systohc.c b/drivers/rtc/systohc.c
> index b4a68ffcd06b..df804597de71 100644
> --- a/drivers/rtc/systohc.c
> +++ b/drivers/rtc/systohc.c
> @@ -26,10 +26,7 @@ int rtc_set_ntp_time(struct timespec64 now)
>  	struct rtc_time tm;
>  	int err = -ENODEV;
>  
> -	if (now.tv_nsec < (NSEC_PER_SEC >> 1))
> -		rtc_time64_to_tm(now.tv_sec, &tm);
> -	else
> -		rtc_time64_to_tm(now.tv_sec + 1, &tm);
> +	rtc_time64_to_tm(now.tv_sec, &tm);
>  
>  	rtc = rtc_class_open(CONFIG_RTC_SYSTOHC_DEVICE);
>  	if (rtc) {
> diff --git a/kernel/sysctl.c b/kernel/sysctl.c
> index 6648fbbb8157..c2ce802f7ab9 100644
> --- a/kernel/sysctl.c
> +++ b/kernel/sysctl.c
> @@ -98,6 +98,9 @@
>  
>  #if defined(CONFIG_SYSCTL)
>  
> +extern unsigned int sysctl_ntp_rtc_offset;
> +extern unsigned int sysctl_ntp_rtc_sync;
> +
>  /* External variables not in a header file. */
>  extern int suid_dumpable;
>  #ifdef CONFIG_COREDUMP
> @@ -303,6 +306,24 @@ static int max_extfrag_threshold = 1000;
>  #endif
>  
>  static struct ctl_table kern_table[] = {
> +#if defined(CONFIG_GENERIC_CMOS_UPDATE) || defined(CONFIG_RTC_SYSTOHC)
> +	{
> +		.procname	= "ntp_rtc_offset",
> +		.data		= &sysctl_ntp_rtc_offset,
> +		.maxlen		= sizeof(sysctl_ntp_rtc_offset),
> +		.mode		= 0644,
> +		.proc_handler	= proc_douintvec,
> +	},
> +	{
> +		.procname	= "ntp_rtc_sync",
> +		.data		= &sysctl_ntp_rtc_sync,
> +		.maxlen		= sizeof(sysctl_ntp_rtc_sync),
> +		.mode		= 0644,
> +		.proc_handler	= proc_douintvec_minmax,
> +		.extra1		= &zero,
> +		.extra2		= &one,
> +	},
> +#endif
>  	{
>  		.procname	= "sched_child_runs_first",
>  		.data		= &sysctl_sched_child_runs_first,
> diff --git a/kernel/time/ntp.c b/kernel/time/ntp.c
> index edf19cc53140..674c45d30561 100644
> --- a/kernel/time/ntp.c
> +++ b/kernel/time/ntp.c
> @@ -508,6 +508,9 @@ int __weak update_persistent_clock64(struct timespec64 now64)
>  #endif
>  
>  #if defined(CONFIG_GENERIC_CMOS_UPDATE) || defined(CONFIG_RTC_SYSTOHC)
> +unsigned long sysctl_ntp_rtc_offset = NSEC_PER_SEC / 2;
> +unsigned int sysctl_ntp_rtc_sync = true;
> +
>  static void sync_cmos_clock(struct work_struct *work);
>  
>  static DECLARE_DELAYED_WORK(sync_cmos_work, sync_cmos_clock);
> @@ -526,7 +529,7 @@ static void sync_cmos_clock(struct work_struct *work)
>  	 * may not expire at the correct time.  Thus, we adjust...
>  	 * We want the clock to be within a couple of ticks from the target.
>  	 */
> -	if (!ntp_synced()) {
> +	if (!ntp_synced() || !sysctl_ntp_rtc_sync) {
>  		/*
>  		 * Not synced, exit, do not restart a timer (if one is
>  		 * running, let it run out).
> @@ -535,7 +538,7 @@ static void sync_cmos_clock(struct work_struct *work)
>  	}
>  
>  	getnstimeofday64(&now);
> -	if (abs(now.tv_nsec - (NSEC_PER_SEC / 2)) <= tick_nsec * 5) {
> +	if (abs(now.tv_nsec - sysctl_ntp_rtc_offset) <= tick_nsec * 5) {
>  		struct timespec64 adjust = now;
>  
>  		fail = -ENODEV;
> @@ -551,7 +554,7 @@ static void sync_cmos_clock(struct work_struct *work)
>  #endif
>  	}
>  
> -	next.tv_nsec = (NSEC_PER_SEC / 2) - now.tv_nsec - (TICK_NSEC / 2);
> +	next.tv_nsec = sysctl_ntp_rtc_offset - now.tv_nsec - (TICK_NSEC / 2);
>  	if (next.tv_nsec <= 0)
>  		next.tv_nsec += NSEC_PER_SEC;
>  
> 
> 
> -- 
> RMK's Patch system: http://www.armlinux.org.uk/developer/patches/
> FTTC broadband for 0.8mile line in suburbia: sync at 8.8Mbps down 630kbps up
> According to speedtest.net: 8.21Mbps down 510kbps up
> 
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

-- 
RMK's Patch system: http://www.armlinux.org.uk/developer/patches/
FTTC broadband for 0.8mile line in suburbia: sync at 8.8Mbps down 630kbps up
According to speedtest.net: 8.21Mbps down 510kbps up



More information about the linux-arm-kernel mailing list