diff options
Diffstat (limited to 'kernel/time/ntp.c')
| -rw-r--r-- | kernel/time/ntp.c | 61 | 
1 files changed, 54 insertions, 7 deletions
diff --git a/kernel/time/ntp.c b/kernel/time/ntp.c index 7a681003001c..fb4d98c7fd43 100644 --- a/kernel/time/ntp.c +++ b/kernel/time/ntp.c @@ -35,6 +35,7 @@ unsigned long			tick_nsec;  static u64			tick_length;  static u64			tick_length_base; +#define SECS_PER_DAY		86400  #define MAX_TICKADJ		500LL		/* usecs */  #define MAX_TICKADJ_SCALED \  	(((MAX_TICKADJ * NSEC_PER_USEC) << NTP_SCALE_SHIFT) / NTP_INTERVAL_FREQ) @@ -76,6 +77,9 @@ static long			time_adjust;  /* constant (boot-param configurable) NTP tick adjustment (upscaled)	*/  static s64			ntp_tick_adj; +/* second value of the next pending leapsecond, or TIME64_MAX if no leap */ +static time64_t			ntp_next_leap_sec = TIME64_MAX; +  #ifdef CONFIG_NTP_PPS  /* @@ -349,6 +353,7 @@ void ntp_clear(void)  	tick_length	= tick_length_base;  	time_offset	= 0; +	ntp_next_leap_sec = TIME64_MAX;  	/* Clear PPS state variables */  	pps_clear();  } @@ -359,6 +364,21 @@ u64 ntp_tick_length(void)  	return tick_length;  } +/** + * ntp_get_next_leap - Returns the next leapsecond in CLOCK_REALTIME ktime_t + * + * Provides the time of the next leapsecond against CLOCK_REALTIME in + * a ktime_t format. Returns KTIME_MAX if no leapsecond is pending. + */ +ktime_t ntp_get_next_leap(void) +{ +	ktime_t ret; + +	if ((time_state == TIME_INS) && (time_status & STA_INS)) +		return ktime_set(ntp_next_leap_sec, 0); +	ret.tv64 = KTIME_MAX; +	return ret; +}  /*   * this routine handles the overflow of the microsecond field @@ -382,15 +402,21 @@ int second_overflow(unsigned long secs)  	 */  	switch (time_state) {  	case TIME_OK: -		if (time_status & STA_INS) +		if (time_status & STA_INS) {  			time_state = TIME_INS; -		else if (time_status & STA_DEL) +			ntp_next_leap_sec = secs + SECS_PER_DAY - +						(secs % SECS_PER_DAY); +		} else if (time_status & STA_DEL) {  			time_state = TIME_DEL; +			ntp_next_leap_sec = secs + SECS_PER_DAY - +						 ((secs+1) % SECS_PER_DAY); +		}  		break;  	case TIME_INS: -		if (!(time_status & STA_INS)) +		if (!(time_status & STA_INS)) { +			ntp_next_leap_sec = TIME64_MAX;  			time_state = TIME_OK; -		else if (secs % 86400 == 0) { +		} else if (secs % SECS_PER_DAY == 0) {  			leap = -1;  			time_state = TIME_OOP;  			printk(KERN_NOTICE @@ -398,19 +424,21 @@ int second_overflow(unsigned long secs)  		}  		break;  	case TIME_DEL: -		if (!(time_status & STA_DEL)) +		if (!(time_status & STA_DEL)) { +			ntp_next_leap_sec = TIME64_MAX;  			time_state = TIME_OK; -		else if ((secs + 1) % 86400 == 0) { +		} else if ((secs + 1) % SECS_PER_DAY == 0) {  			leap = 1; +			ntp_next_leap_sec = TIME64_MAX;  			time_state = TIME_WAIT;  			printk(KERN_NOTICE  				"Clock: deleting leap second 23:59:59 UTC\n");  		}  		break;  	case TIME_OOP: +		ntp_next_leap_sec = TIME64_MAX;  		time_state = TIME_WAIT;  		break; -  	case TIME_WAIT:  		if (!(time_status & (STA_INS | STA_DEL)))  			time_state = TIME_OK; @@ -547,6 +575,7 @@ static inline void process_adj_status(struct timex *txc, struct timespec64 *ts)  	if ((time_status & STA_PLL) && !(txc->status & STA_PLL)) {  		time_state = TIME_OK;  		time_status = STA_UNSYNC; +		ntp_next_leap_sec = TIME64_MAX;  		/* restart PPS frequency calibration */  		pps_reset_freq_interval();  	} @@ -711,6 +740,24 @@ int __do_adjtimex(struct timex *txc, struct timespec64 *ts, s32 *time_tai)  	if (!(time_status & STA_NANO))  		txc->time.tv_usec /= NSEC_PER_USEC; +	/* Handle leapsec adjustments */ +	if (unlikely(ts->tv_sec >= ntp_next_leap_sec)) { +		if ((time_state == TIME_INS) && (time_status & STA_INS)) { +			result = TIME_OOP; +			txc->tai++; +			txc->time.tv_sec--; +		} +		if ((time_state == TIME_DEL) && (time_status & STA_DEL)) { +			result = TIME_WAIT; +			txc->tai--; +			txc->time.tv_sec++; +		} +		if ((time_state == TIME_OOP) && +					(ts->tv_sec == ntp_next_leap_sec)) { +			result = TIME_WAIT; +		} +	} +  	return result;  }  |