diff options
author | Roman Zippel <zippel@linux-m68k.org> | 2008-05-01 07:34:41 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2008-05-01 11:03:59 -0400 |
commit | 7dffa3c673fbcf835cd7be80bb4aec8ad3f51168 (patch) | |
tree | 63264208ed97f18a74a5a7cd2e100cc2c4e13449 /kernel/time | |
parent | 8383c42399f394a89bd6c2f03632c53689bdde7a (diff) |
ntp: handle leap second via timer
Remove the leap second handling from second_overflow(), which doesn't have to
check for it every second anymore. With CONFIG_NO_HZ this also makes sure the
leap second is handled close to the full second. Additionally this makes it
possible to abort a leap second properly by resetting the STA_INS/STA_DEL
status bits.
Signed-off-by: Roman Zippel <zippel@linux-m68k.org>
Cc: john stultz <johnstul@us.ibm.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'kernel/time')
-rw-r--r-- | kernel/time/ntp.c | 133 | ||||
-rw-r--r-- | kernel/time/timekeeping.c | 4 |
2 files changed, 92 insertions, 45 deletions
diff --git a/kernel/time/ntp.c b/kernel/time/ntp.c index df9718bac8d0..5125ddd8196b 100644 --- a/kernel/time/ntp.c +++ b/kernel/time/ntp.c | |||
@@ -16,6 +16,7 @@ | |||
16 | #include <linux/hrtimer.h> | 16 | #include <linux/hrtimer.h> |
17 | #include <linux/capability.h> | 17 | #include <linux/capability.h> |
18 | #include <linux/math64.h> | 18 | #include <linux/math64.h> |
19 | #include <linux/clocksource.h> | ||
19 | #include <asm/timex.h> | 20 | #include <asm/timex.h> |
20 | 21 | ||
21 | /* | 22 | /* |
@@ -26,6 +27,8 @@ unsigned long tick_nsec; /* ACTHZ period (nsec) */ | |||
26 | u64 tick_length; | 27 | u64 tick_length; |
27 | static u64 tick_length_base; | 28 | static u64 tick_length_base; |
28 | 29 | ||
30 | static struct hrtimer leap_timer; | ||
31 | |||
29 | #define MAX_TICKADJ 500 /* microsecs */ | 32 | #define MAX_TICKADJ 500 /* microsecs */ |
30 | #define MAX_TICKADJ_SCALED (((u64)(MAX_TICKADJ * NSEC_PER_USEC) << \ | 33 | #define MAX_TICKADJ_SCALED (((u64)(MAX_TICKADJ * NSEC_PER_USEC) << \ |
31 | NTP_SCALE_SHIFT) / NTP_INTERVAL_FREQ) | 34 | NTP_SCALE_SHIFT) / NTP_INTERVAL_FREQ) |
@@ -120,64 +123,70 @@ void ntp_clear(void) | |||
120 | } | 123 | } |
121 | 124 | ||
122 | /* | 125 | /* |
123 | * this routine handles the overflow of the microsecond field | 126 | * Leap second processing. If in leap-insert state at the end of the |
124 | * | 127 | * day, the system clock is set back one second; if in leap-delete |
125 | * The tricky bits of code to handle the accurate clock support | 128 | * state, the system clock is set ahead one second. |
126 | * were provided by Dave Mills (Mills@UDEL.EDU) of NTP fame. | ||
127 | * They were originally developed for SUN and DEC kernels. | ||
128 | * All the kudos should go to Dave for this stuff. | ||
129 | */ | 129 | */ |
130 | void second_overflow(void) | 130 | static enum hrtimer_restart ntp_leap_second(struct hrtimer *timer) |
131 | { | 131 | { |
132 | s64 time_adj; | 132 | enum hrtimer_restart res = HRTIMER_NORESTART; |
133 | 133 | ||
134 | /* Bump the maxerror field */ | 134 | write_seqlock_irq(&xtime_lock); |
135 | time_maxerror += MAXFREQ / NSEC_PER_USEC; | ||
136 | if (time_maxerror > NTP_PHASE_LIMIT) { | ||
137 | time_maxerror = NTP_PHASE_LIMIT; | ||
138 | time_status |= STA_UNSYNC; | ||
139 | } | ||
140 | 135 | ||
141 | /* | ||
142 | * Leap second processing. If in leap-insert state at the end of the | ||
143 | * day, the system clock is set back one second; if in leap-delete | ||
144 | * state, the system clock is set ahead one second. The microtime() | ||
145 | * routine or external clock driver will insure that reported time is | ||
146 | * always monotonic. The ugly divides should be replaced. | ||
147 | */ | ||
148 | switch (time_state) { | 136 | switch (time_state) { |
149 | case TIME_OK: | 137 | case TIME_OK: |
150 | if (time_status & STA_INS) | ||
151 | time_state = TIME_INS; | ||
152 | else if (time_status & STA_DEL) | ||
153 | time_state = TIME_DEL; | ||
154 | break; | 138 | break; |
155 | case TIME_INS: | 139 | case TIME_INS: |
156 | if (xtime.tv_sec % 86400 == 0) { | 140 | xtime.tv_sec--; |
157 | xtime.tv_sec--; | 141 | wall_to_monotonic.tv_sec++; |
158 | wall_to_monotonic.tv_sec++; | 142 | time_state = TIME_OOP; |
159 | time_state = TIME_OOP; | 143 | printk(KERN_NOTICE "Clock: " |
160 | printk(KERN_NOTICE "Clock: inserting leap second " | 144 | "inserting leap second 23:59:60 UTC\n"); |
161 | "23:59:60 UTC\n"); | 145 | leap_timer.expires = ktime_add_ns(leap_timer.expires, |
162 | } | 146 | NSEC_PER_SEC); |
147 | res = HRTIMER_RESTART; | ||
163 | break; | 148 | break; |
164 | case TIME_DEL: | 149 | case TIME_DEL: |
165 | if ((xtime.tv_sec + 1) % 86400 == 0) { | 150 | xtime.tv_sec++; |
166 | xtime.tv_sec++; | 151 | time_tai--; |
167 | time_tai--; | 152 | wall_to_monotonic.tv_sec--; |
168 | wall_to_monotonic.tv_sec--; | 153 | time_state = TIME_WAIT; |
169 | time_state = TIME_WAIT; | 154 | printk(KERN_NOTICE "Clock: " |
170 | printk(KERN_NOTICE "Clock: deleting leap second " | 155 | "deleting leap second 23:59:59 UTC\n"); |
171 | "23:59:59 UTC\n"); | ||
172 | } | ||
173 | break; | 156 | break; |
174 | case TIME_OOP: | 157 | case TIME_OOP: |
175 | time_tai++; | 158 | time_tai++; |
176 | time_state = TIME_WAIT; | 159 | time_state = TIME_WAIT; |
177 | break; | 160 | /* fall through */ |
178 | case TIME_WAIT: | 161 | case TIME_WAIT: |
179 | if (!(time_status & (STA_INS | STA_DEL))) | 162 | if (!(time_status & (STA_INS | STA_DEL))) |
180 | time_state = TIME_OK; | 163 | time_state = TIME_OK; |
164 | break; | ||
165 | } | ||
166 | update_vsyscall(&xtime, clock); | ||
167 | |||
168 | write_sequnlock_irq(&xtime_lock); | ||
169 | |||
170 | return res; | ||
171 | } | ||
172 | |||
173 | /* | ||
174 | * this routine handles the overflow of the microsecond field | ||
175 | * | ||
176 | * The tricky bits of code to handle the accurate clock support | ||
177 | * were provided by Dave Mills (Mills@UDEL.EDU) of NTP fame. | ||
178 | * They were originally developed for SUN and DEC kernels. | ||
179 | * All the kudos should go to Dave for this stuff. | ||
180 | */ | ||
181 | void second_overflow(void) | ||
182 | { | ||
183 | s64 time_adj; | ||
184 | |||
185 | /* Bump the maxerror field */ | ||
186 | time_maxerror += MAXFREQ / NSEC_PER_USEC; | ||
187 | if (time_maxerror > NTP_PHASE_LIMIT) { | ||
188 | time_maxerror = NTP_PHASE_LIMIT; | ||
189 | time_status |= STA_UNSYNC; | ||
181 | } | 190 | } |
182 | 191 | ||
183 | /* | 192 | /* |
@@ -268,7 +277,7 @@ static inline void notify_cmos_timer(void) { } | |||
268 | int do_adjtimex(struct timex *txc) | 277 | int do_adjtimex(struct timex *txc) |
269 | { | 278 | { |
270 | struct timespec ts; | 279 | struct timespec ts; |
271 | long save_adjust; | 280 | long save_adjust, sec; |
272 | int result; | 281 | int result; |
273 | 282 | ||
274 | /* In order to modify anything, you gotta be super-user! */ | 283 | /* In order to modify anything, you gotta be super-user! */ |
@@ -289,6 +298,10 @@ int do_adjtimex(struct timex *txc) | |||
289 | txc->tick > 1100000/USER_HZ) | 298 | txc->tick > 1100000/USER_HZ) |
290 | return -EINVAL; | 299 | return -EINVAL; |
291 | 300 | ||
301 | if (time_state != TIME_OK && txc->modes & ADJ_STATUS) | ||
302 | hrtimer_cancel(&leap_timer); | ||
303 | getnstimeofday(&ts); | ||
304 | |||
292 | write_seqlock_irq(&xtime_lock); | 305 | write_seqlock_irq(&xtime_lock); |
293 | 306 | ||
294 | /* Save for later - semantics of adjtime is to return old value */ | 307 | /* Save for later - semantics of adjtime is to return old value */ |
@@ -305,6 +318,34 @@ int do_adjtimex(struct timex *txc) | |||
305 | /* only set allowed bits */ | 318 | /* only set allowed bits */ |
306 | time_status &= STA_RONLY; | 319 | time_status &= STA_RONLY; |
307 | time_status |= txc->status & ~STA_RONLY; | 320 | time_status |= txc->status & ~STA_RONLY; |
321 | |||
322 | switch (time_state) { | ||
323 | case TIME_OK: | ||
324 | start_timer: | ||
325 | sec = ts.tv_sec; | ||
326 | if (time_status & STA_INS) { | ||
327 | time_state = TIME_INS; | ||
328 | sec += 86400 - sec % 86400; | ||
329 | hrtimer_start(&leap_timer, ktime_set(sec, 0), HRTIMER_MODE_ABS); | ||
330 | } else if (time_status & STA_DEL) { | ||
331 | time_state = TIME_DEL; | ||
332 | sec += 86400 - (sec + 1) % 86400; | ||
333 | hrtimer_start(&leap_timer, ktime_set(sec, 0), HRTIMER_MODE_ABS); | ||
334 | } | ||
335 | break; | ||
336 | case TIME_INS: | ||
337 | case TIME_DEL: | ||
338 | time_state = TIME_OK; | ||
339 | goto start_timer; | ||
340 | break; | ||
341 | case TIME_WAIT: | ||
342 | if (!(time_status & (STA_INS | STA_DEL))) | ||
343 | time_state = TIME_OK; | ||
344 | break; | ||
345 | case TIME_OOP: | ||
346 | hrtimer_restart(&leap_timer); | ||
347 | break; | ||
348 | } | ||
308 | } | 349 | } |
309 | 350 | ||
310 | if (txc->modes & ADJ_NANO) | 351 | if (txc->modes & ADJ_NANO) |
@@ -384,7 +425,6 @@ int do_adjtimex(struct timex *txc) | |||
384 | txc->stbcnt = 0; | 425 | txc->stbcnt = 0; |
385 | write_sequnlock_irq(&xtime_lock); | 426 | write_sequnlock_irq(&xtime_lock); |
386 | 427 | ||
387 | getnstimeofday(&ts); | ||
388 | txc->time.tv_sec = ts.tv_sec; | 428 | txc->time.tv_sec = ts.tv_sec; |
389 | txc->time.tv_usec = ts.tv_nsec; | 429 | txc->time.tv_usec = ts.tv_nsec; |
390 | if (!(time_status & STA_NANO)) | 430 | if (!(time_status & STA_NANO)) |
@@ -402,3 +442,10 @@ static int __init ntp_tick_adj_setup(char *str) | |||
402 | } | 442 | } |
403 | 443 | ||
404 | __setup("ntp_tick_adj=", ntp_tick_adj_setup); | 444 | __setup("ntp_tick_adj=", ntp_tick_adj_setup); |
445 | |||
446 | void __init ntp_init(void) | ||
447 | { | ||
448 | ntp_clear(); | ||
449 | hrtimer_init(&leap_timer, CLOCK_REALTIME, HRTIMER_MODE_ABS); | ||
450 | leap_timer.function = ntp_leap_second; | ||
451 | } | ||
diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index 7e74d8092067..e91c29f961c9 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c | |||
@@ -53,7 +53,7 @@ void update_xtime_cache(u64 nsec) | |||
53 | timespec_add_ns(&xtime_cache, nsec); | 53 | timespec_add_ns(&xtime_cache, nsec); |
54 | } | 54 | } |
55 | 55 | ||
56 | static struct clocksource *clock; /* pointer to current clocksource */ | 56 | struct clocksource *clock; |
57 | 57 | ||
58 | 58 | ||
59 | #ifdef CONFIG_GENERIC_TIME | 59 | #ifdef CONFIG_GENERIC_TIME |
@@ -246,7 +246,7 @@ void __init timekeeping_init(void) | |||
246 | 246 | ||
247 | write_seqlock_irqsave(&xtime_lock, flags); | 247 | write_seqlock_irqsave(&xtime_lock, flags); |
248 | 248 | ||
249 | ntp_clear(); | 249 | ntp_init(); |
250 | 250 | ||
251 | clock = clocksource_get_next(); | 251 | clock = clocksource_get_next(); |
252 | clocksource_calculate_interval(clock, NTP_INTERVAL_LENGTH); | 252 | clocksource_calculate_interval(clock, NTP_INTERVAL_LENGTH); |