CVSROOT:        /cvs
Module name:    src
Changes by:     chel...@cvs.openbsd.org 2020/07/16 17:06:43

Modified files:
        sys/kern       : kern_tc.c 

Log message:
adjtime(2): distribute skew along arbitrary period on runtime clock

The adjtime(2) adjustment is applied at up to 5000ppm/sec from
tc_windup().  At the start of each UTC second, ntp_update_second() is
called from tc_windup() and up to 5000ppm worth of skew is deducted
from the timehands' th_adjtimedelta member and moved to the
th_adjustment member.  The resulting th_adjustment value is then mixed
into the th_scale member and thus the system UTC time is slowly nudged
in a particular direction.

This works pretty well.  The only issues have to do with the use of
the the edge of the UTC second as the start of the ntp_update_second()
period:

1. If the UTC clock jumps forward we can get stuck in a loop calling
ntp_update_second() from tc_windup().  We work around this with
a magic number, LARGE_STEP.  If the UTC clock jumps forward more
than LARGE_STEP seconds we truncate the number of iterations to 2.

Per the comment in tc_windup(), we do 2 iterations instead of 1
iteration to account for a leap second we may have missed.  This is
an anachronism: the OpenBSD kernel does not handle leap seconds
anymore.

Such jumps happen during settimeofday(2), during boot when we jump
the clock from zero to the RTC time, and during resume when we jump
the clock to the RTC time (again).  They are unavoidable.

2. Changes to adjtime(2) are applied asynchronously.  For example, if
you try to cancel the ongoing adjustment...

struct timeval zero = { 0, 0 };

adjtime(&zero, NULL);

... it can take up to one second for the adjustment to be cancelled.
In the meantime, the skew continues.  This delayed application is not
intuitive or documented.

3. Adjustment is deducted from th_adjtimedelta across suspends of fewer
than LARGE_STEP seconds, even though we do not skew the clock while
we are suspended.  This is unintuitive, incorrect, and undocumented.

We can avoid all of these problems by applying the adjustment along
an arbitrary period on the runtime clock instead of the UTC clock.

1. The runtime clock doesn't jump arbitrary amounts, so we never get
stuck in a loop and we don't need a magic number to test for this
possibility.  With the removal of the magic number LARGE_STEP we
can also remove the leap second handling from the tc_windup() code.

2. With a new timehands member, th_next_ntp_update, we can track when
the next ntp_update_second() call should happen on the runtime clock.
This value can be updated during the adjtime(2) system call, so
changes to the skew happen *immediately* instead of up to one second
after the adjtime(2) call.

3. The runtime clock does not jump across a suspend: no skew is
deducted from th_adjtimedelta for any time we are offline and
unable to adjust the clock.

otto@ says the use of the runtime clock should not be a problem for
ntpd(8) or the NTP algorithm in general.

Reply via email to