diff options
author | imp <imp@FreeBSD.org> | 2003-06-25 21:23:51 +0000 |
---|---|---|
committer | imp <imp@FreeBSD.org> | 2003-06-25 21:23:51 +0000 |
commit | 9f82a8a978d4814e87fa50e1b0efe691a4ffbd38 (patch) | |
tree | 13312dc2f6b866a0f6f6007a592fe0671dd1741e /sys/kern | |
parent | 6f40b91a361a55ea10fe77a8b6186ca04ccc0672 (diff) | |
download | FreeBSD-src-9f82a8a978d4814e87fa50e1b0efe691a4ffbd38.zip FreeBSD-src-9f82a8a978d4814e87fa50e1b0efe691a4ffbd38.tar.gz |
Fix leap second processing by the kernel time keeping routines.
Before, we would add/subtract the leap second when the system had been
up for an even multiple of days, rather than at the end of the day, as
a leap second is defined (at least wrt ntp). We do this by
calculating the notion of UTC earlier in the loop, and passing that to
get it adjusted. Any adjustments that ntp_update_second makes to this
time are then transferred to boot time. We can't pass it either the
boot time or the uptime because their sum is what determines when a
leap second is needed. This code adds an extra assignment and two
extra compare in the typical case, which is as cheap as I could made
it.
I have confirmed with this code the kernel time does the correct thing
for both positive and negative leap seconds. Since the ntp interface
doesn't allow for +2 or -2, those cases can't be tested (and the folks
in the know here say there will never be a +2s or -2s leap event, but
rather two +1s or -1s leap events).
There will very likely be no leap seconds for a while, given how the
earth is speeding up and slowing down, so there will be plenty of time
for this fix to propigate. UT1-UTC is currently at "about -0.4s" and
decrementing by .1s every 8 months or so. 6 * 8 is 48 months, or 4
years.
-stable has different code, but a similar bug that was introduced
about the time of the last leap second, which is why nobody has
noticed until now.
MFC After: 3 weeks
Reviewed by: phk
"Furthermore, leap seconds must die." -- Cato the Elder
Diffstat (limited to 'sys/kern')
-rw-r--r-- | sys/kern/kern_tc.c | 33 |
1 files changed, 27 insertions, 6 deletions
diff --git a/sys/kern/kern_tc.c b/sys/kern/kern_tc.c index 29d2f74..167ba20 100644 --- a/sys/kern/kern_tc.c +++ b/sys/kern/kern_tc.c @@ -21,6 +21,14 @@ __FBSDID("$FreeBSD$"); #include <sys/timex.h> /* + * a large step happens on boot. This constant detects such + * a steps. It is relatively small so that ntp_update_second gets called + * enough in the typical 'missed a couple of seconds' case, but doesn't + * loop forever when the time step is large. + */ +#define LARGE_STEP 200 + +/* * Implement a dummy timecounter which we can use until we get a real one * in the air. This allows the console and other early stuff to use * time services. @@ -344,6 +352,7 @@ tc_windup(void) u_int64_t scale; u_int delta, ncount, ogen; int i; + time_t t; /* * Make the next timehands a copy of the current one, but do not @@ -381,13 +390,28 @@ tc_windup(void) if (tho->th_counter->tc_poll_pps) tho->th_counter->tc_poll_pps(tho->th_counter); + /* + * Compute the UTC time, before any leapsecond adjustments, are + * made. + */ + bt = th->th_offset; + bintime_add(&bt, &boottimebin); + /* * Deal with NTP second processing. The for loop normally only * iterates once, but in extreme situations it might keep NTP sane - * if timeouts are not run for several seconds. + * if timeouts are not run for several seconds. At boot, the + * time step can be large when the TOD hardware has been read, so + * on really large steps, we call ntp_update_second only once. */ - for (i = th->th_offset.sec - tho->th_offset.sec; i > 0; i--) - ntp_update_second(&th->th_adjustment, &th->th_offset.sec); + for (i = bt.sec - tho->th_microtime.tv_sec; i > 0; i--) { + t = bt.sec; + ntp_update_second(&th->th_adjustment, &bt.sec); + if (bt.sec != t) + boottimebin.sec += bt.sec - t; + if (i > LARGE_STEP) + break; + } /* Now is a good time to change timecounters. */ if (th->th_counter != timecounter) { @@ -423,9 +447,6 @@ tc_windup(void) scale /= th->th_counter->tc_frequency; th->th_scale = scale * 2; - /* Update the UTC timestamps used for the get*() functions. */ - bt = th->th_offset; - bintime_add(&bt, &boottimebin); bintime2timeval(&bt, &th->th_microtime); bintime2timespec(&bt, &th->th_nanotime); |