diff options
Diffstat (limited to 'kernel/time/timekeeping.c')
| -rw-r--r-- | kernel/time/timekeeping.c | 153 | 
1 files changed, 77 insertions, 76 deletions
diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index 946acb72179f..30b7a409bf1e 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -118,18 +118,6 @@ static inline void tk_update_sleep_time(struct timekeeper *tk, ktime_t delta)  #ifdef CONFIG_DEBUG_TIMEKEEPING  #define WARNING_FREQ (HZ*300) /* 5 minute rate-limiting */ -/* - * These simple flag variables are managed - * without locks, which is racy, but ok since - * we don't really care about being super - * precise about how many events were seen, - * just that a problem was observed. - */ -static int timekeeping_underflow_seen; -static int timekeeping_overflow_seen; - -/* last_warning is only modified under the timekeeping lock */ -static long timekeeping_last_warning;  static void timekeeping_check_update(struct timekeeper *tk, cycle_t offset)  { @@ -149,29 +137,30 @@ static void timekeeping_check_update(struct timekeeper *tk, cycle_t offset)  		}  	} -	if (timekeeping_underflow_seen) { -		if (jiffies - timekeeping_last_warning > WARNING_FREQ) { +	if (tk->underflow_seen) { +		if (jiffies - tk->last_warning > WARNING_FREQ) {  			printk_deferred("WARNING: Underflow in clocksource '%s' observed, time update ignored.\n", name);  			printk_deferred("         Please report this, consider using a different clocksource, if possible.\n");  			printk_deferred("         Your kernel is probably still fine.\n"); -			timekeeping_last_warning = jiffies; +			tk->last_warning = jiffies;  		} -		timekeeping_underflow_seen = 0; +		tk->underflow_seen = 0;  	} -	if (timekeeping_overflow_seen) { -		if (jiffies - timekeeping_last_warning > WARNING_FREQ) { +	if (tk->overflow_seen) { +		if (jiffies - tk->last_warning > WARNING_FREQ) {  			printk_deferred("WARNING: Overflow in clocksource '%s' observed, time update capped.\n", name);  			printk_deferred("         Please report this, consider using a different clocksource, if possible.\n");  			printk_deferred("         Your kernel is probably still fine.\n"); -			timekeeping_last_warning = jiffies; +			tk->last_warning = jiffies;  		} -		timekeeping_overflow_seen = 0; +		tk->overflow_seen = 0;  	}  }  static inline cycle_t timekeeping_get_delta(struct tk_read_base *tkr)  { +	struct timekeeper *tk = &tk_core.timekeeper;  	cycle_t now, last, mask, max, delta;  	unsigned int seq; @@ -197,13 +186,13 @@ static inline cycle_t timekeeping_get_delta(struct tk_read_base *tkr)  	 * mask-relative negative values.  	 */  	if (unlikely((~delta & mask) < (mask >> 3))) { -		timekeeping_underflow_seen = 1; +		tk->underflow_seen = 1;  		delta = 0;  	}  	/* Cap delta value to the max_cycles values to avoid mult overflows */  	if (unlikely(delta > max)) { -		timekeeping_overflow_seen = 1; +		tk->overflow_seen = 1;  		delta = tkr->clock->max_cycles;  	} @@ -551,6 +540,17 @@ int pvclock_gtod_unregister_notifier(struct notifier_block *nb)  EXPORT_SYMBOL_GPL(pvclock_gtod_unregister_notifier);  /* + * tk_update_leap_state - helper to update the next_leap_ktime + */ +static inline void tk_update_leap_state(struct timekeeper *tk) +{ +	tk->next_leap_ktime = ntp_get_next_leap(); +	if (tk->next_leap_ktime.tv64 != KTIME_MAX) +		/* Convert to monotonic time */ +		tk->next_leap_ktime = ktime_sub(tk->next_leap_ktime, tk->offs_real); +} + +/*   * Update the ktime_t based scalar nsec members of the timekeeper   */  static inline void tk_update_ktime_data(struct timekeeper *tk) @@ -591,17 +591,25 @@ static void timekeeping_update(struct timekeeper *tk, unsigned int action)  		ntp_clear();  	} +	tk_update_leap_state(tk);  	tk_update_ktime_data(tk);  	update_vsyscall(tk);  	update_pvclock_gtod(tk, action & TK_CLOCK_WAS_SET); +	update_fast_timekeeper(&tk->tkr_mono, &tk_fast_mono); +	update_fast_timekeeper(&tk->tkr_raw,  &tk_fast_raw); + +	if (action & TK_CLOCK_WAS_SET) +		tk->clock_was_set_seq++; +	/* +	 * The mirroring of the data to the shadow-timekeeper needs +	 * to happen last here to ensure we don't over-write the +	 * timekeeper structure on the next update with stale data +	 */  	if (action & TK_MIRROR)  		memcpy(&shadow_timekeeper, &tk_core.timekeeper,  		       sizeof(tk_core.timekeeper)); - -	update_fast_timekeeper(&tk->tkr_mono, &tk_fast_mono); -	update_fast_timekeeper(&tk->tkr_raw,  &tk_fast_raw);  }  /** @@ -699,6 +707,23 @@ ktime_t ktime_get(void)  }  EXPORT_SYMBOL_GPL(ktime_get); +u32 ktime_get_resolution_ns(void) +{ +	struct timekeeper *tk = &tk_core.timekeeper; +	unsigned int seq; +	u32 nsecs; + +	WARN_ON(timekeeping_suspended); + +	do { +		seq = read_seqcount_begin(&tk_core.seq); +		nsecs = tk->tkr_mono.mult >> tk->tkr_mono.shift; +	} while (read_seqcount_retry(&tk_core.seq, seq)); + +	return nsecs; +} +EXPORT_SYMBOL_GPL(ktime_get_resolution_ns); +  static ktime_t *offsets[TK_OFFS_MAX] = {  	[TK_OFFS_REAL]	= &tk_core.timekeeper.offs_real,  	[TK_OFFS_BOOT]	= &tk_core.timekeeper.offs_boot, @@ -1179,28 +1204,20 @@ void __weak read_persistent_clock64(struct timespec64 *ts64)  }  /** - * read_boot_clock -  Return time of the system start. + * read_boot_clock64 -  Return time of the system start.   *   * Weak dummy function for arches that do not yet support it.   * Function to read the exact time the system has been started. - * Returns a timespec with tv_sec=0 and tv_nsec=0 if unsupported. + * Returns a timespec64 with tv_sec=0 and tv_nsec=0 if unsupported.   *   *  XXX - Do be sure to remove it once all arches implement it.   */ -void __weak read_boot_clock(struct timespec *ts) +void __weak read_boot_clock64(struct timespec64 *ts)  {  	ts->tv_sec = 0;  	ts->tv_nsec = 0;  } -void __weak read_boot_clock64(struct timespec64 *ts64) -{ -	struct timespec ts; - -	read_boot_clock(&ts); -	*ts64 = timespec_to_timespec64(ts); -} -  /* Flag for if timekeeping_resume() has injected sleeptime */  static bool sleeptime_injected; @@ -1836,8 +1853,9 @@ void update_wall_time(void)  	 * memcpy under the tk_core.seq against one before we start  	 * updating.  	 */ +	timekeeping_update(tk, clock_set);  	memcpy(real_tk, tk, sizeof(*tk)); -	timekeeping_update(real_tk, clock_set); +	/* The memcpy must come last. Do not put anything here! */  	write_seqcount_end(&tk_core.seq);  out:  	raw_spin_unlock_irqrestore(&timekeeper_lock, flags); @@ -1926,47 +1944,20 @@ void do_timer(unsigned long ticks)  }  /** - * ktime_get_update_offsets_tick - hrtimer helper - * @offs_real:	pointer to storage for monotonic -> realtime offset - * @offs_boot:	pointer to storage for monotonic -> boottime offset - * @offs_tai:	pointer to storage for monotonic -> clock tai offset - * - * Returns monotonic time at last tick and various offsets - */ -ktime_t ktime_get_update_offsets_tick(ktime_t *offs_real, ktime_t *offs_boot, -							ktime_t *offs_tai) -{ -	struct timekeeper *tk = &tk_core.timekeeper; -	unsigned int seq; -	ktime_t base; -	u64 nsecs; - -	do { -		seq = read_seqcount_begin(&tk_core.seq); - -		base = tk->tkr_mono.base; -		nsecs = tk->tkr_mono.xtime_nsec >> tk->tkr_mono.shift; - -		*offs_real = tk->offs_real; -		*offs_boot = tk->offs_boot; -		*offs_tai = tk->offs_tai; -	} while (read_seqcount_retry(&tk_core.seq, seq)); - -	return ktime_add_ns(base, nsecs); -} - -#ifdef CONFIG_HIGH_RES_TIMERS -/**   * ktime_get_update_offsets_now - hrtimer helper + * @cwsseq:	pointer to check and store the clock was set sequence number   * @offs_real:	pointer to storage for monotonic -> realtime offset   * @offs_boot:	pointer to storage for monotonic -> boottime offset   * @offs_tai:	pointer to storage for monotonic -> clock tai offset   * - * Returns current monotonic time and updates the offsets + * Returns current monotonic time and updates the offsets if the + * sequence number in @cwsseq and timekeeper.clock_was_set_seq are + * different. + *   * Called from hrtimer_interrupt() or retrigger_next_event()   */ -ktime_t ktime_get_update_offsets_now(ktime_t *offs_real, ktime_t *offs_boot, -							ktime_t *offs_tai) +ktime_t ktime_get_update_offsets_now(unsigned int *cwsseq, ktime_t *offs_real, +				     ktime_t *offs_boot, ktime_t *offs_tai)  {  	struct timekeeper *tk = &tk_core.timekeeper;  	unsigned int seq; @@ -1978,15 +1969,23 @@ ktime_t ktime_get_update_offsets_now(ktime_t *offs_real, ktime_t *offs_boot,  		base = tk->tkr_mono.base;  		nsecs = timekeeping_get_ns(&tk->tkr_mono); +		base = ktime_add_ns(base, nsecs); + +		if (*cwsseq != tk->clock_was_set_seq) { +			*cwsseq = tk->clock_was_set_seq; +			*offs_real = tk->offs_real; +			*offs_boot = tk->offs_boot; +			*offs_tai = tk->offs_tai; +		} + +		/* Handle leapsecond insertion adjustments */ +		if (unlikely(base.tv64 >= tk->next_leap_ktime.tv64)) +			*offs_real = ktime_sub(tk->offs_real, ktime_set(1, 0)); -		*offs_real = tk->offs_real; -		*offs_boot = tk->offs_boot; -		*offs_tai = tk->offs_tai;  	} while (read_seqcount_retry(&tk_core.seq, seq)); -	return ktime_add_ns(base, nsecs); +	return base;  } -#endif  /**   * do_adjtimex() - Accessor function to NTP __do_adjtimex function @@ -2027,6 +2026,8 @@ int do_adjtimex(struct timex *txc)  		__timekeeping_set_tai_offset(tk, tai);  		timekeeping_update(tk, TK_MIRROR | TK_CLOCK_WAS_SET);  	} +	tk_update_leap_state(tk); +  	write_seqcount_end(&tk_core.seq);  	raw_spin_unlock_irqrestore(&timekeeper_lock, flags);  |