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); |
