summaryrefslogtreecommitdiffstats
path: root/sys/kern
diff options
context:
space:
mode:
authorimp <imp@FreeBSD.org>2003-06-25 21:23:51 +0000
committerimp <imp@FreeBSD.org>2003-06-25 21:23:51 +0000
commit9f82a8a978d4814e87fa50e1b0efe691a4ffbd38 (patch)
tree13312dc2f6b866a0f6f6007a592fe0671dd1741e /sys/kern
parent6f40b91a361a55ea10fe77a8b6186ca04ccc0672 (diff)
downloadFreeBSD-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.c33
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);
OpenPOWER on IntegriCloud