aboutsummaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
authorRoman Zippel <zippel@linux-m68k.org>2008-05-01 07:34:41 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2008-05-01 11:03:59 -0400
commit7dffa3c673fbcf835cd7be80bb4aec8ad3f51168 (patch)
tree63264208ed97f18a74a5a7cd2e100cc2c4e13449 /kernel
parent8383c42399f394a89bd6c2f03632c53689bdde7a (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')
-rw-r--r--kernel/time/ntp.c133
-rw-r--r--kernel/time/timekeeping.c4
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) */
26u64 tick_length; 27u64 tick_length;
27static u64 tick_length_base; 28static u64 tick_length_base;
28 29
30static 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 */
130void second_overflow(void) 130static 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 */
181void 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) { }
268int do_adjtimex(struct timex *txc) 277int 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
446void __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
56static struct clocksource *clock; /* pointer to current clocksource */ 56struct 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);