|
@@ -115,6 +115,7 @@ static void tk_xtime_add(struct timekeeper *tk, const struct timespec *ts)
|
|
|
{
|
|
|
tk->xtime_sec += ts->tv_sec;
|
|
|
tk->xtime_nsec += (u64)ts->tv_nsec << tk->shift;
|
|
|
+ tk_normalize_xtime(tk);
|
|
|
}
|
|
|
|
|
|
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)
|
|
|
tk->xtime_nsec += cycle_delta * tk->mult;
|
|
|
|
|
|
/* If arch requires, add in gettimeoffset() */
|
|
|
- tk->xtime_nsec += arch_gettimeoffset() << tk->shift;
|
|
|
+ tk->xtime_nsec += (u64)arch_gettimeoffset() << tk->shift;
|
|
|
|
|
|
tk_normalize_xtime(tk);
|
|
|
|
|
@@ -427,7 +428,7 @@ int do_settimeofday(const struct timespec *tv)
|
|
|
struct timespec ts_delta, xt;
|
|
|
unsigned long flags;
|
|
|
|
|
|
- if ((unsigned long)tv->tv_nsec >= NSEC_PER_SEC)
|
|
|
+ if (!timespec_valid(tv))
|
|
|
return -EINVAL;
|
|
|
|
|
|
write_seqlock_irqsave(&tk->lock, flags);
|
|
@@ -463,6 +464,8 @@ int timekeeping_inject_offset(struct timespec *ts)
|
|
|
{
|
|
|
struct timekeeper *tk = &timekeeper;
|
|
|
unsigned long flags;
|
|
|
+ struct timespec tmp;
|
|
|
+ int ret = 0;
|
|
|
|
|
|
if ((unsigned long)ts->tv_nsec >= NSEC_PER_SEC)
|
|
|
return -EINVAL;
|
|
@@ -471,10 +474,17 @@ int timekeeping_inject_offset(struct timespec *ts)
|
|
|
|
|
|
timekeeping_forward_now(tk);
|
|
|
|
|
|
+ /* Make sure the proposed value is valid */
|
|
|
+ tmp = timespec_add(tk_xtime(tk), *ts);
|
|
|
+ if (!timespec_valid(&tmp)) {
|
|
|
+ ret = -EINVAL;
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
|
|
|
tk_xtime_add(tk, ts);
|
|
|
tk_set_wall_to_mono(tk, timespec_sub(tk->wall_to_monotonic, *ts));
|
|
|
|
|
|
+error: /* even if we error out, we forwarded the time, so call update */
|
|
|
timekeeping_update(tk, true);
|
|
|
|
|
|
write_sequnlock_irqrestore(&tk->lock, flags);
|
|
@@ -482,7 +492,7 @@ int timekeeping_inject_offset(struct timespec *ts)
|
|
|
/* signal hrtimers about time change */
|
|
|
clock_was_set();
|
|
|
|
|
|
- return 0;
|
|
|
+ return ret;
|
|
|
}
|
|
|
EXPORT_SYMBOL(timekeeping_inject_offset);
|
|
|
|
|
@@ -649,7 +659,20 @@ void __init timekeeping_init(void)
|
|
|
struct timespec now, boot, tmp;
|
|
|
|
|
|
read_persistent_clock(&now);
|
|
|
+ if (!timespec_valid(&now)) {
|
|
|
+ pr_warn("WARNING: Persistent clock returned invalid value!\n"
|
|
|
+ " Check your CMOS/BIOS settings.\n");
|
|
|
+ now.tv_sec = 0;
|
|
|
+ now.tv_nsec = 0;
|
|
|
+ }
|
|
|
+
|
|
|
read_boot_clock(&boot);
|
|
|
+ if (!timespec_valid(&boot)) {
|
|
|
+ pr_warn("WARNING: Boot clock returned invalid value!\n"
|
|
|
+ " Check your CMOS/BIOS settings.\n");
|
|
|
+ boot.tv_sec = 0;
|
|
|
+ boot.tv_nsec = 0;
|
|
|
+ }
|
|
|
|
|
|
seqlock_init(&tk->lock);
|
|
|
|
|
@@ -1129,6 +1152,10 @@ static void update_wall_time(void)
|
|
|
offset = (clock->read(clock) - clock->cycle_last) & clock->mask;
|
|
|
#endif
|
|
|
|
|
|
+ /* Check if there's really nothing to do */
|
|
|
+ if (offset < tk->cycle_interval)
|
|
|
+ goto out;
|
|
|
+
|
|
|
/*
|
|
|
* With NO_HZ we may have to accumulate many cycle_intervals
|
|
|
* (think "ticks") worth of time at once. To do this efficiently,
|
|
@@ -1161,9 +1188,9 @@ static void update_wall_time(void)
|
|
|
* the vsyscall implementations are converted to use xtime_nsec
|
|
|
* (shifted nanoseconds), this can be killed.
|
|
|
*/
|
|
|
- remainder = tk->xtime_nsec & ((1 << tk->shift) - 1);
|
|
|
+ remainder = tk->xtime_nsec & ((1ULL << tk->shift) - 1);
|
|
|
tk->xtime_nsec -= remainder;
|
|
|
- tk->xtime_nsec += 1 << tk->shift;
|
|
|
+ tk->xtime_nsec += 1ULL << tk->shift;
|
|
|
tk->ntp_error += remainder << tk->ntp_error_shift;
|
|
|
|
|
|
/*
|