diff options
Diffstat (limited to 'kernel/time/timekeeping.c')
| -rw-r--r-- | kernel/time/timekeeping.c | 37 |
1 files changed, 32 insertions, 5 deletions
diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index e16af197a2bc..0c1485e42be6 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 | ||
| @@ -427,7 +428,7 @@ int do_settimeofday(const struct timespec *tv) | |||
| 427 | struct timespec ts_delta, xt; | 428 | struct timespec ts_delta, xt; |
| 428 | unsigned long flags; | 429 | unsigned long flags; |
| 429 | 430 | ||
| 430 | if ((unsigned long)tv->tv_nsec >= NSEC_PER_SEC) | 431 | if (!timespec_valid(tv)) |
| 431 | return -EINVAL; | 432 | return -EINVAL; |
| 432 | 433 | ||
| 433 | write_seqlock_irqsave(&tk->lock, flags); | 434 | write_seqlock_irqsave(&tk->lock, flags); |
| @@ -463,6 +464,8 @@ int timekeeping_inject_offset(struct timespec *ts) | |||
| 463 | { | 464 | { |
| 464 | struct timekeeper *tk = &timekeeper; | 465 | struct timekeeper *tk = &timekeeper; |
| 465 | unsigned long flags; | 466 | unsigned long flags; |
| 467 | struct timespec tmp; | ||
| 468 | int ret = 0; | ||
| 466 | 469 | ||
| 467 | if ((unsigned long)ts->tv_nsec >= NSEC_PER_SEC) | 470 | if ((unsigned long)ts->tv_nsec >= NSEC_PER_SEC) |
| 468 | return -EINVAL; | 471 | return -EINVAL; |
| @@ -471,10 +474,17 @@ int timekeeping_inject_offset(struct timespec *ts) | |||
| 471 | 474 | ||
| 472 | timekeeping_forward_now(tk); | 475 | timekeeping_forward_now(tk); |
| 473 | 476 | ||
| 477 | /* Make sure the proposed value is valid */ | ||
| 478 | tmp = timespec_add(tk_xtime(tk), *ts); | ||
| 479 | if (!timespec_valid(&tmp)) { | ||
| 480 | ret = -EINVAL; | ||
| 481 | goto error; | ||
| 482 | } | ||
| 474 | 483 | ||
| 475 | tk_xtime_add(tk, ts); | 484 | tk_xtime_add(tk, ts); |
| 476 | tk_set_wall_to_mono(tk, timespec_sub(tk->wall_to_monotonic, *ts)); | 485 | tk_set_wall_to_mono(tk, timespec_sub(tk->wall_to_monotonic, *ts)); |
| 477 | 486 | ||
| 487 | error: /* even if we error out, we forwarded the time, so call update */ | ||
| 478 | timekeeping_update(tk, true); | 488 | timekeeping_update(tk, true); |
| 479 | 489 | ||
| 480 | write_sequnlock_irqrestore(&tk->lock, flags); | 490 | write_sequnlock_irqrestore(&tk->lock, flags); |
| @@ -482,7 +492,7 @@ int timekeeping_inject_offset(struct timespec *ts) | |||
| 482 | /* signal hrtimers about time change */ | 492 | /* signal hrtimers about time change */ |
| 483 | clock_was_set(); | 493 | clock_was_set(); |
| 484 | 494 | ||
| 485 | return 0; | 495 | return ret; |
| 486 | } | 496 | } |
| 487 | EXPORT_SYMBOL(timekeeping_inject_offset); | 497 | EXPORT_SYMBOL(timekeeping_inject_offset); |
| 488 | 498 | ||
| @@ -649,7 +659,20 @@ void __init timekeeping_init(void) | |||
| 649 | struct timespec now, boot, tmp; | 659 | struct timespec now, boot, tmp; |
| 650 | 660 | ||
| 651 | read_persistent_clock(&now); | 661 | read_persistent_clock(&now); |
| 662 | if (!timespec_valid(&now)) { | ||
| 663 | pr_warn("WARNING: Persistent clock returned invalid value!\n" | ||
| 664 | " Check your CMOS/BIOS settings.\n"); | ||
| 665 | now.tv_sec = 0; | ||
| 666 | now.tv_nsec = 0; | ||
| 667 | } | ||
| 668 | |||
| 652 | read_boot_clock(&boot); | 669 | read_boot_clock(&boot); |
| 670 | if (!timespec_valid(&boot)) { | ||
| 671 | pr_warn("WARNING: Boot clock returned invalid value!\n" | ||
| 672 | " Check your CMOS/BIOS settings.\n"); | ||
| 673 | boot.tv_sec = 0; | ||
| 674 | boot.tv_nsec = 0; | ||
| 675 | } | ||
| 653 | 676 | ||
| 654 | seqlock_init(&tk->lock); | 677 | seqlock_init(&tk->lock); |
| 655 | 678 | ||
| @@ -1129,6 +1152,10 @@ static void update_wall_time(void) | |||
| 1129 | offset = (clock->read(clock) - clock->cycle_last) & clock->mask; | 1152 | offset = (clock->read(clock) - clock->cycle_last) & clock->mask; |
| 1130 | #endif | 1153 | #endif |
| 1131 | 1154 | ||
| 1155 | /* Check if there's really nothing to do */ | ||
| 1156 | if (offset < tk->cycle_interval) | ||
| 1157 | goto out; | ||
| 1158 | |||
| 1132 | /* | 1159 | /* |
| 1133 | * With NO_HZ we may have to accumulate many cycle_intervals | 1160 | * With NO_HZ we may have to accumulate many cycle_intervals |
| 1134 | * (think "ticks") worth of time at once. To do this efficiently, | 1161 | * (think "ticks") worth of time at once. To do this efficiently, |
| @@ -1161,9 +1188,9 @@ static void update_wall_time(void) | |||
| 1161 | * the vsyscall implementations are converted to use xtime_nsec | 1188 | * the vsyscall implementations are converted to use xtime_nsec |
| 1162 | * (shifted nanoseconds), this can be killed. | 1189 | * (shifted nanoseconds), this can be killed. |
| 1163 | */ | 1190 | */ |
| 1164 | remainder = tk->xtime_nsec & ((1 << tk->shift) - 1); | 1191 | remainder = tk->xtime_nsec & ((1ULL << tk->shift) - 1); |
| 1165 | tk->xtime_nsec -= remainder; | 1192 | tk->xtime_nsec -= remainder; |
| 1166 | tk->xtime_nsec += 1 << tk->shift; | 1193 | tk->xtime_nsec += 1ULL << tk->shift; |
| 1167 | tk->ntp_error += remainder << tk->ntp_error_shift; | 1194 | tk->ntp_error += remainder << tk->ntp_error_shift; |
| 1168 | 1195 | ||
| 1169 | /* | 1196 | /* |
