diff options
Diffstat (limited to 'kernel/time/timekeeping.c')
-rw-r--r-- | kernel/time/timekeeping.c | 58 |
1 files changed, 45 insertions, 13 deletions
diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index e16af197a2bc..d3b91e75cecd 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c | |||
@@ -115,6 +115,7 @@ static void tk_xtime_add(struct timekeeper *tk, const struct timespec *ts) | |||
115 | { | 115 | { |
116 | tk->xtime_sec += ts->tv_sec; | 116 | tk->xtime_sec += ts->tv_sec; |
117 | tk->xtime_nsec += (u64)ts->tv_nsec << tk->shift; | 117 | tk->xtime_nsec += (u64)ts->tv_nsec << tk->shift; |
118 | tk_normalize_xtime(tk); | ||
118 | } | 119 | } |
119 | 120 | ||
120 | static void tk_set_wall_to_mono(struct timekeeper *tk, struct timespec wtm) | 121 | static void tk_set_wall_to_mono(struct timekeeper *tk, struct timespec wtm) |
@@ -276,7 +277,7 @@ static void timekeeping_forward_now(struct timekeeper *tk) | |||
276 | tk->xtime_nsec += cycle_delta * tk->mult; | 277 | tk->xtime_nsec += cycle_delta * tk->mult; |
277 | 278 | ||
278 | /* If arch requires, add in gettimeoffset() */ | 279 | /* If arch requires, add in gettimeoffset() */ |
279 | tk->xtime_nsec += arch_gettimeoffset() << tk->shift; | 280 | tk->xtime_nsec += (u64)arch_gettimeoffset() << tk->shift; |
280 | 281 | ||
281 | tk_normalize_xtime(tk); | 282 | tk_normalize_xtime(tk); |
282 | 283 | ||
@@ -302,10 +303,11 @@ void getnstimeofday(struct timespec *ts) | |||
302 | seq = read_seqbegin(&tk->lock); | 303 | seq = read_seqbegin(&tk->lock); |
303 | 304 | ||
304 | ts->tv_sec = tk->xtime_sec; | 305 | ts->tv_sec = tk->xtime_sec; |
305 | ts->tv_nsec = timekeeping_get_ns(tk); | 306 | nsecs = timekeeping_get_ns(tk); |
306 | 307 | ||
307 | } while (read_seqretry(&tk->lock, seq)); | 308 | } while (read_seqretry(&tk->lock, seq)); |
308 | 309 | ||
310 | ts->tv_nsec = 0; | ||
309 | timespec_add_ns(ts, nsecs); | 311 | timespec_add_ns(ts, nsecs); |
310 | } | 312 | } |
311 | EXPORT_SYMBOL(getnstimeofday); | 313 | EXPORT_SYMBOL(getnstimeofday); |
@@ -344,6 +346,7 @@ void ktime_get_ts(struct timespec *ts) | |||
344 | { | 346 | { |
345 | struct timekeeper *tk = &timekeeper; | 347 | struct timekeeper *tk = &timekeeper; |
346 | struct timespec tomono; | 348 | struct timespec tomono; |
349 | s64 nsec; | ||
347 | unsigned int seq; | 350 | unsigned int seq; |
348 | 351 | ||
349 | WARN_ON(timekeeping_suspended); | 352 | WARN_ON(timekeeping_suspended); |
@@ -351,13 +354,14 @@ void ktime_get_ts(struct timespec *ts) | |||
351 | do { | 354 | do { |
352 | seq = read_seqbegin(&tk->lock); | 355 | seq = read_seqbegin(&tk->lock); |
353 | ts->tv_sec = tk->xtime_sec; | 356 | ts->tv_sec = tk->xtime_sec; |
354 | ts->tv_nsec = timekeeping_get_ns(tk); | 357 | nsec = timekeeping_get_ns(tk); |
355 | tomono = tk->wall_to_monotonic; | 358 | tomono = tk->wall_to_monotonic; |
356 | 359 | ||
357 | } while (read_seqretry(&tk->lock, seq)); | 360 | } while (read_seqretry(&tk->lock, seq)); |
358 | 361 | ||
359 | set_normalized_timespec(ts, ts->tv_sec + tomono.tv_sec, | 362 | ts->tv_sec += tomono.tv_sec; |
360 | ts->tv_nsec + tomono.tv_nsec); | 363 | ts->tv_nsec = 0; |
364 | timespec_add_ns(ts, nsec + tomono.tv_nsec); | ||
361 | } | 365 | } |
362 | EXPORT_SYMBOL_GPL(ktime_get_ts); | 366 | EXPORT_SYMBOL_GPL(ktime_get_ts); |
363 | 367 | ||
@@ -427,7 +431,7 @@ int do_settimeofday(const struct timespec *tv) | |||
427 | struct timespec ts_delta, xt; | 431 | struct timespec ts_delta, xt; |
428 | unsigned long flags; | 432 | unsigned long flags; |
429 | 433 | ||
430 | if ((unsigned long)tv->tv_nsec >= NSEC_PER_SEC) | 434 | if (!timespec_valid_strict(tv)) |
431 | return -EINVAL; | 435 | return -EINVAL; |
432 | 436 | ||
433 | write_seqlock_irqsave(&tk->lock, flags); | 437 | write_seqlock_irqsave(&tk->lock, flags); |
@@ -463,6 +467,8 @@ int timekeeping_inject_offset(struct timespec *ts) | |||
463 | { | 467 | { |
464 | struct timekeeper *tk = &timekeeper; | 468 | struct timekeeper *tk = &timekeeper; |
465 | unsigned long flags; | 469 | unsigned long flags; |
470 | struct timespec tmp; | ||
471 | int ret = 0; | ||
466 | 472 | ||
467 | if ((unsigned long)ts->tv_nsec >= NSEC_PER_SEC) | 473 | if ((unsigned long)ts->tv_nsec >= NSEC_PER_SEC) |
468 | return -EINVAL; | 474 | return -EINVAL; |
@@ -471,10 +477,17 @@ int timekeeping_inject_offset(struct timespec *ts) | |||
471 | 477 | ||
472 | timekeeping_forward_now(tk); | 478 | timekeeping_forward_now(tk); |
473 | 479 | ||
480 | /* Make sure the proposed value is valid */ | ||
481 | tmp = timespec_add(tk_xtime(tk), *ts); | ||
482 | if (!timespec_valid_strict(&tmp)) { | ||
483 | ret = -EINVAL; | ||
484 | goto error; | ||
485 | } | ||
474 | 486 | ||
475 | tk_xtime_add(tk, ts); | 487 | tk_xtime_add(tk, ts); |
476 | tk_set_wall_to_mono(tk, timespec_sub(tk->wall_to_monotonic, *ts)); | 488 | tk_set_wall_to_mono(tk, timespec_sub(tk->wall_to_monotonic, *ts)); |
477 | 489 | ||
490 | error: /* even if we error out, we forwarded the time, so call update */ | ||
478 | timekeeping_update(tk, true); | 491 | timekeeping_update(tk, true); |
479 | 492 | ||
480 | write_sequnlock_irqrestore(&tk->lock, flags); | 493 | write_sequnlock_irqrestore(&tk->lock, flags); |
@@ -482,7 +495,7 @@ int timekeeping_inject_offset(struct timespec *ts) | |||
482 | /* signal hrtimers about time change */ | 495 | /* signal hrtimers about time change */ |
483 | clock_was_set(); | 496 | clock_was_set(); |
484 | 497 | ||
485 | return 0; | 498 | return ret; |
486 | } | 499 | } |
487 | EXPORT_SYMBOL(timekeeping_inject_offset); | 500 | EXPORT_SYMBOL(timekeeping_inject_offset); |
488 | 501 | ||
@@ -649,7 +662,20 @@ void __init timekeeping_init(void) | |||
649 | struct timespec now, boot, tmp; | 662 | struct timespec now, boot, tmp; |
650 | 663 | ||
651 | read_persistent_clock(&now); | 664 | read_persistent_clock(&now); |
665 | if (!timespec_valid_strict(&now)) { | ||
666 | pr_warn("WARNING: Persistent clock returned invalid value!\n" | ||
667 | " Check your CMOS/BIOS settings.\n"); | ||
668 | now.tv_sec = 0; | ||
669 | now.tv_nsec = 0; | ||
670 | } | ||
671 | |||
652 | read_boot_clock(&boot); | 672 | read_boot_clock(&boot); |
673 | if (!timespec_valid_strict(&boot)) { | ||
674 | pr_warn("WARNING: Boot clock returned invalid value!\n" | ||
675 | " Check your CMOS/BIOS settings.\n"); | ||
676 | boot.tv_sec = 0; | ||
677 | boot.tv_nsec = 0; | ||
678 | } | ||
653 | 679 | ||
654 | seqlock_init(&tk->lock); | 680 | seqlock_init(&tk->lock); |
655 | 681 | ||
@@ -690,7 +716,7 @@ static struct timespec timekeeping_suspend_time; | |||
690 | static void __timekeeping_inject_sleeptime(struct timekeeper *tk, | 716 | static void __timekeeping_inject_sleeptime(struct timekeeper *tk, |
691 | struct timespec *delta) | 717 | struct timespec *delta) |
692 | { | 718 | { |
693 | if (!timespec_valid(delta)) { | 719 | if (!timespec_valid_strict(delta)) { |
694 | printk(KERN_WARNING "__timekeeping_inject_sleeptime: Invalid " | 720 | printk(KERN_WARNING "__timekeeping_inject_sleeptime: Invalid " |
695 | "sleep delta value!\n"); | 721 | "sleep delta value!\n"); |
696 | return; | 722 | return; |
@@ -1129,6 +1155,10 @@ static void update_wall_time(void) | |||
1129 | offset = (clock->read(clock) - clock->cycle_last) & clock->mask; | 1155 | offset = (clock->read(clock) - clock->cycle_last) & clock->mask; |
1130 | #endif | 1156 | #endif |
1131 | 1157 | ||
1158 | /* Check if there's really nothing to do */ | ||
1159 | if (offset < tk->cycle_interval) | ||
1160 | goto out; | ||
1161 | |||
1132 | /* | 1162 | /* |
1133 | * With NO_HZ we may have to accumulate many cycle_intervals | 1163 | * With NO_HZ we may have to accumulate many cycle_intervals |
1134 | * (think "ticks") worth of time at once. To do this efficiently, | 1164 | * (think "ticks") worth of time at once. To do this efficiently, |
@@ -1161,9 +1191,9 @@ static void update_wall_time(void) | |||
1161 | * the vsyscall implementations are converted to use xtime_nsec | 1191 | * the vsyscall implementations are converted to use xtime_nsec |
1162 | * (shifted nanoseconds), this can be killed. | 1192 | * (shifted nanoseconds), this can be killed. |
1163 | */ | 1193 | */ |
1164 | remainder = tk->xtime_nsec & ((1 << tk->shift) - 1); | 1194 | remainder = tk->xtime_nsec & ((1ULL << tk->shift) - 1); |
1165 | tk->xtime_nsec -= remainder; | 1195 | tk->xtime_nsec -= remainder; |
1166 | tk->xtime_nsec += 1 << tk->shift; | 1196 | tk->xtime_nsec += 1ULL << tk->shift; |
1167 | tk->ntp_error += remainder << tk->ntp_error_shift; | 1197 | tk->ntp_error += remainder << tk->ntp_error_shift; |
1168 | 1198 | ||
1169 | /* | 1199 | /* |
@@ -1217,6 +1247,7 @@ void get_monotonic_boottime(struct timespec *ts) | |||
1217 | { | 1247 | { |
1218 | struct timekeeper *tk = &timekeeper; | 1248 | struct timekeeper *tk = &timekeeper; |
1219 | struct timespec tomono, sleep; | 1249 | struct timespec tomono, sleep; |
1250 | s64 nsec; | ||
1220 | unsigned int seq; | 1251 | unsigned int seq; |
1221 | 1252 | ||
1222 | WARN_ON(timekeeping_suspended); | 1253 | WARN_ON(timekeeping_suspended); |
@@ -1224,14 +1255,15 @@ void get_monotonic_boottime(struct timespec *ts) | |||
1224 | do { | 1255 | do { |
1225 | seq = read_seqbegin(&tk->lock); | 1256 | seq = read_seqbegin(&tk->lock); |
1226 | ts->tv_sec = tk->xtime_sec; | 1257 | ts->tv_sec = tk->xtime_sec; |
1227 | ts->tv_nsec = timekeeping_get_ns(tk); | 1258 | nsec = timekeeping_get_ns(tk); |
1228 | tomono = tk->wall_to_monotonic; | 1259 | tomono = tk->wall_to_monotonic; |
1229 | sleep = tk->total_sleep_time; | 1260 | sleep = tk->total_sleep_time; |
1230 | 1261 | ||
1231 | } while (read_seqretry(&tk->lock, seq)); | 1262 | } while (read_seqretry(&tk->lock, seq)); |
1232 | 1263 | ||
1233 | set_normalized_timespec(ts, ts->tv_sec + tomono.tv_sec + sleep.tv_sec, | 1264 | ts->tv_sec += tomono.tv_sec + sleep.tv_sec; |
1234 | ts->tv_nsec + tomono.tv_nsec + sleep.tv_nsec); | 1265 | ts->tv_nsec = 0; |
1266 | timespec_add_ns(ts, nsec + tomono.tv_nsec + sleep.tv_nsec); | ||
1235 | } | 1267 | } |
1236 | EXPORT_SYMBOL_GPL(get_monotonic_boottime); | 1268 | EXPORT_SYMBOL_GPL(get_monotonic_boottime); |
1237 | 1269 | ||