diff options
Diffstat (limited to 'kernel/time/timekeeping.c')
| -rw-r--r-- | kernel/time/timekeeping.c | 39 | 
1 files changed, 33 insertions, 6 deletions
diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index e16af197a2b..34e5eac8142 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)  {  	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_strict(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_strict(&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_strict(&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_strict(&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); @@ -690,7 +713,7 @@ static struct timespec timekeeping_suspend_time;  static void __timekeeping_inject_sleeptime(struct timekeeper *tk,  							struct timespec *delta)  { -	if (!timespec_valid(delta)) { +	if (!timespec_valid_strict(delta)) {  		printk(KERN_WARNING "__timekeeping_inject_sleeptime: Invalid "  					"sleep delta value!\n");  		return; @@ -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;  	/*  |