Contributed by mbalmer on from the timekeeping dept.
I recently imported frequency correction code from dragonfly, whith some changes: only do frequency compensation if the clock is synced, and a slightly diffent way of computing the linear regression. But let me explain a little more how things are done...
While mostly a userland hacker, recently I have been working on time code in the kernel: I helped mickey@ make time behave better on i386 SMP systems. I also have code to fix time going backwards on amd64 SMP systems, but this code is not ready for prime time yet. All this time stuff got me interested in ntpd(8) again.
ntpd(8) calls adjtime(2) to set the clock. adjtime(2) causes a temporary slowdown or speedup of the clock, until the requested adjustment is reached.
ntpd used to overcompensate, especially on systems with a large clock skew, caused by using old offset data from before a call to adjtime(2). I wrote code to correct that, which needed to be able to get the current amount of adjustment still underway. Sadly, the adjtime(2) did not have a way to get the current adjustment without setting one, so I added that.
While working on this I found out that DragonFlyBSD already had this overcompensating correction code and after evaluating it, I decided to use that version.
Then one big problem area remained: most computers have a clock that drifts: without being able to adjust the clock frequency, ntpd will be adjusting the time continuously, and between update intervals, time is not accurate.
I first looked at the ntp.org method of doing things, which i very complex and looks overengineered. Some people might need this, but in general I felt there should be a simpler method. Looking again at the DragonFlyBSD code, I found the basis of kernel changes to introduce a permanent skew in the clock. This code I could not use directly, since OpenBSD has a slightly different way of keeping time compared to DragonFlyBSD. But the idea was sound, and I found a way to implement it without using 64 bit divides, which would not have been acceptable in the hardlock() code, so adjfreq(2) was introduced: a system call that allows slight adjustments in the clock frequency.
The corresponding ntpd(8) changes are also based on the DragonFlyBSD code. The code computes a linear regression on the adjustments being done by ntpd(8) to compute a frequency correction. I took that code and made some changes: frequency corrections are only done if the clock is synced, and I changed a few things in the linear regression code to make it more clear. Various developers helped with testing, especially ckuethe@, who did very extensive tests and collected nice statistics.
All this now has the consequence that time will be much more accurate. From one of my test machines:
$ rdate -npv ntp1.nl.net Sat Jun 17 21:03:56 CEST 2006 rdate: adjust local clock by -0.000840 seconds $
The reported offset will vary, but in general stays below 1ms. ntpd(8) is now even able to make the clock in my Mac mini behave:
Jun 17 21:17:46 fonzo ntpd: adjusting clock frequency by -0.190821 to 2877.689515ppm
This large correction is needed since the time base reported by Open Firmware is incorrect. Quite a few Macs suffer from this.
To try this, you will need a recent kernel, a recent libc and a recent ntpd. Snapshots made after Jun 17 should contain the full new functionality. It takes some time for ntpd to collect data to do the frequency correction, so be patient. The last step was to add code to remember the frequency adjustments between reboots.
(Comments are closed)