Merge branch 'fortglx/4.15/time' of https://git.linaro.org/people/john.stultz/linux into timers/core

Pull timekeeping updates from John Stultz:

 - More y2038 work from Arnd Bergmann

 - A new mechanism to allow RTC drivers to specify the resolution of the
   RTC so the suspend/resume code can make informed decisions whether to
   inject the suspended time or not in case of fast suspend/resume cycles.
This commit is contained in:
Thomas Gleixner
2017-10-31 23:17:28 +01:00
14 changed files with 687 additions and 617 deletions

View File

@@ -492,6 +492,67 @@ out:
return leap;
}
static void sync_hw_clock(struct work_struct *work);
static DECLARE_DELAYED_WORK(sync_work, sync_hw_clock);
static void sched_sync_hw_clock(struct timespec64 now,
unsigned long target_nsec, bool fail)
{
struct timespec64 next;
getnstimeofday64(&next);
if (!fail)
next.tv_sec = 659;
else {
/*
* Try again as soon as possible. Delaying long periods
* decreases the accuracy of the work queue timer. Due to this
* the algorithm is very likely to require a short-sleep retry
* after the above long sleep to synchronize ts_nsec.
*/
next.tv_sec = 0;
}
/* Compute the needed delay that will get to tv_nsec == target_nsec */
next.tv_nsec = target_nsec - next.tv_nsec;
if (next.tv_nsec <= 0)
next.tv_nsec += NSEC_PER_SEC;
if (next.tv_nsec >= NSEC_PER_SEC) {
next.tv_sec++;
next.tv_nsec -= NSEC_PER_SEC;
}
queue_delayed_work(system_power_efficient_wq, &sync_work,
timespec64_to_jiffies(&next));
}
static void sync_rtc_clock(void)
{
unsigned long target_nsec;
struct timespec64 adjust, now;
int rc;
if (!IS_ENABLED(CONFIG_RTC_SYSTOHC))
return;
getnstimeofday64(&now);
adjust = now;
if (persistent_clock_is_local)
adjust.tv_sec -= (sys_tz.tz_minuteswest * 60);
/*
* The current RTC in use will provide the target_nsec it wants to be
* called at, and does rtc_tv_nsec_ok internally.
*/
rc = rtc_set_ntp_time(adjust, &target_nsec);
if (rc == -ENODEV)
return;
sched_sync_hw_clock(now, target_nsec, rc);
}
#ifdef CONFIG_GENERIC_CMOS_UPDATE
int __weak update_persistent_clock(struct timespec now)
{
@@ -507,77 +568,76 @@ int __weak update_persistent_clock64(struct timespec64 now64)
}
#endif
#if defined(CONFIG_GENERIC_CMOS_UPDATE) || defined(CONFIG_RTC_SYSTOHC)
static void sync_cmos_clock(struct work_struct *work);
static DECLARE_DELAYED_WORK(sync_cmos_work, sync_cmos_clock);
static void sync_cmos_clock(struct work_struct *work)
static bool sync_cmos_clock(void)
{
static bool no_cmos;
struct timespec64 now;
struct timespec64 next;
int fail = 1;
struct timespec64 adjust;
int rc = -EPROTO;
long target_nsec = NSEC_PER_SEC / 2;
if (!IS_ENABLED(CONFIG_GENERIC_CMOS_UPDATE))
return false;
if (no_cmos)
return false;
/*
* If we have an externally synchronized Linux clock, then update
* CMOS clock accordingly every ~11 minutes. Set_rtc_mmss() has to be
* called as close as possible to 500 ms before the new second starts.
* This code is run on a timer. If the clock is set, that timer
* may not expire at the correct time. Thus, we adjust...
* We want the clock to be within a couple of ticks from the target.
* Historically update_persistent_clock64() has followed x86
* semantics, which match the MC146818A/etc RTC. This RTC will store
* 'adjust' and then in .5s it will advance once second.
*
* Architectures are strongly encouraged to use rtclib and not
* implement this legacy API.
*/
if (!ntp_synced()) {
/*
* Not synced, exit, do not restart a timer (if one is
* running, let it run out).
*/
return;
}
getnstimeofday64(&now);
if (abs(now.tv_nsec - (NSEC_PER_SEC / 2)) <= tick_nsec * 5) {
struct timespec64 adjust = now;
fail = -ENODEV;
if (rtc_tv_nsec_ok(-1 * target_nsec, &adjust, &now)) {
if (persistent_clock_is_local)
adjust.tv_sec -= (sys_tz.tz_minuteswest * 60);
#ifdef CONFIG_GENERIC_CMOS_UPDATE
fail = update_persistent_clock64(adjust);
#endif
#ifdef CONFIG_RTC_SYSTOHC
if (fail == -ENODEV)
fail = rtc_set_ntp_time(adjust);
#endif
rc = update_persistent_clock64(adjust);
/*
* The machine does not support update_persistent_clock64 even
* though it defines CONFIG_GENERIC_CMOS_UPDATE.
*/
if (rc == -ENODEV) {
no_cmos = true;
return false;
}
}
next.tv_nsec = (NSEC_PER_SEC / 2) - now.tv_nsec - (TICK_NSEC / 2);
if (next.tv_nsec <= 0)
next.tv_nsec += NSEC_PER_SEC;
sched_sync_hw_clock(now, target_nsec, rc);
return true;
}
if (!fail || fail == -ENODEV)
next.tv_sec = 659;
else
next.tv_sec = 0;
/*
* If we have an externally synchronized Linux clock, then update RTC clock
* accordingly every ~11 minutes. Generally RTCs can only store second
* precision, but many RTCs will adjust the phase of their second tick to
* match the moment of update. This infrastructure arranges to call to the RTC
* set at the correct moment to phase synchronize the RTC second tick over
* with the kernel clock.
*/
static void sync_hw_clock(struct work_struct *work)
{
if (!ntp_synced())
return;
if (next.tv_nsec >= NSEC_PER_SEC) {
next.tv_sec++;
next.tv_nsec -= NSEC_PER_SEC;
}
queue_delayed_work(system_power_efficient_wq,
&sync_cmos_work, timespec64_to_jiffies(&next));
if (sync_cmos_clock())
return;
sync_rtc_clock();
}
void ntp_notify_cmos_timer(void)
{
queue_delayed_work(system_power_efficient_wq, &sync_cmos_work, 0);
if (!ntp_synced())
return;
if (IS_ENABLED(CONFIG_GENERIC_CMOS_UPDATE) ||
IS_ENABLED(CONFIG_RTC_SYSTOHC))
queue_delayed_work(system_power_efficient_wq, &sync_work, 0);
}
#else
void ntp_notify_cmos_timer(void) { }
#endif
/*
* Propagate a new txc->status value into the NTP state:
*/
@@ -653,67 +713,6 @@ static inline void process_adjtimex_modes(struct timex *txc,
}
/**
* ntp_validate_timex - Ensures the timex is ok for use in do_adjtimex
*/
int ntp_validate_timex(struct timex *txc)
{
if (txc->modes & ADJ_ADJTIME) {
/* singleshot must not be used with any other mode bits */
if (!(txc->modes & ADJ_OFFSET_SINGLESHOT))
return -EINVAL;
if (!(txc->modes & ADJ_OFFSET_READONLY) &&
!capable(CAP_SYS_TIME))
return -EPERM;
} else {
/* In order to modify anything, you gotta be super-user! */
if (txc->modes && !capable(CAP_SYS_TIME))
return -EPERM;
/*
* if the quartz is off by more than 10% then
* something is VERY wrong!
*/
if (txc->modes & ADJ_TICK &&
(txc->tick < 900000/USER_HZ ||
txc->tick > 1100000/USER_HZ))
return -EINVAL;
}
if (txc->modes & ADJ_SETOFFSET) {
/* In order to inject time, you gotta be super-user! */
if (!capable(CAP_SYS_TIME))
return -EPERM;
if (txc->modes & ADJ_NANO) {
struct timespec ts;
ts.tv_sec = txc->time.tv_sec;
ts.tv_nsec = txc->time.tv_usec;
if (!timespec_inject_offset_valid(&ts))
return -EINVAL;
} else {
if (!timeval_inject_offset_valid(&txc->time))
return -EINVAL;
}
}
/*
* Check for potential multiplication overflows that can
* only happen on 64-bit systems:
*/
if ((txc->modes & ADJ_FREQUENCY) && (BITS_PER_LONG == 64)) {
if (LLONG_MIN / PPM_SCALE > txc->freq)
return -EINVAL;
if (LLONG_MAX / PPM_SCALE < txc->freq)
return -EINVAL;
}
return 0;
}
/*
* adjtimex mainly allows reading (and writing, if superuser) of
* kernel time-keeping variables. used by xntpd.

View File

@@ -7,7 +7,6 @@ extern void ntp_clear(void);
extern u64 ntp_tick_length(void);
extern ktime_t ntp_get_next_leap(void);
extern int second_overflow(time64_t secs);
extern int ntp_validate_timex(struct timex *);
extern int __do_adjtimex(struct timex *, struct timespec64 *, s32 *);
extern void __hardpps(const struct timespec64 *, const struct timespec64 *);
#endif /* _LINUX_NTP_INTERNAL_H */

View File

@@ -157,40 +157,6 @@ SYSCALL_DEFINE2(gettimeofday, struct timeval __user *, tv,
return 0;
}
/*
* Indicates if there is an offset between the system clock and the hardware
* clock/persistent clock/rtc.
*/
int persistent_clock_is_local;
/*
* Adjust the time obtained from the CMOS to be UTC time instead of
* local time.
*
* This is ugly, but preferable to the alternatives. Otherwise we
* would either need to write a program to do it in /etc/rc (and risk
* confusion if the program gets run more than once; it would also be
* hard to make the program warp the clock precisely n hours) or
* compile in the timezone information into the kernel. Bad, bad....
*
* - TYT, 1992-01-01
*
* The best thing to do is to keep the CMOS clock in universal time (UTC)
* as real UNIX machines always do it. This avoids all headaches about
* daylight saving times and warping kernel clocks.
*/
static inline void warp_clock(void)
{
if (sys_tz.tz_minuteswest != 0) {
struct timespec adjust;
persistent_clock_is_local = 1;
adjust.tv_sec = sys_tz.tz_minuteswest * 60;
adjust.tv_nsec = 0;
timekeeping_inject_offset(&adjust);
}
}
/*
* In case for some reason the CMOS clock has not already been running
* in UTC, but in some local time: The first time we set the timezone,
@@ -224,7 +190,7 @@ int do_sys_settimeofday64(const struct timespec64 *tv, const struct timezone *tz
if (firsttime) {
firsttime = 0;
if (!tv)
warp_clock();
timekeeping_warp_clock();
}
}
if (tv)
@@ -441,6 +407,7 @@ time64_t mktime64(const unsigned int year0, const unsigned int mon0,
}
EXPORT_SYMBOL(mktime64);
#if __BITS_PER_LONG == 32
/**
* set_normalized_timespec - set timespec sec and nsec parts and normalize
*
@@ -501,6 +468,7 @@ struct timespec ns_to_timespec(const s64 nsec)
return ts;
}
EXPORT_SYMBOL(ns_to_timespec);
#endif
/**
* ns_to_timeval - Convert nanoseconds to timeval
@@ -520,7 +488,6 @@ struct timeval ns_to_timeval(const s64 nsec)
}
EXPORT_SYMBOL(ns_to_timeval);
#if BITS_PER_LONG == 32
/**
* set_normalized_timespec - set timespec sec and nsec parts and normalize
*
@@ -581,7 +548,7 @@ struct timespec64 ns_to_timespec64(const s64 nsec)
return ts;
}
EXPORT_SYMBOL(ns_to_timespec64);
#endif
/**
* msecs_to_jiffies: - convert milliseconds to jiffies
* @m: time in milliseconds
@@ -852,24 +819,6 @@ unsigned long nsecs_to_jiffies(u64 n)
}
EXPORT_SYMBOL_GPL(nsecs_to_jiffies);
/*
* Add two timespec values and do a safety check for overflow.
* It's assumed that both values are valid (>= 0)
*/
struct timespec timespec_add_safe(const struct timespec lhs,
const struct timespec rhs)
{
struct timespec res;
set_normalized_timespec(&res, lhs.tv_sec + rhs.tv_sec,
lhs.tv_nsec + rhs.tv_nsec);
if (res.tv_sec < lhs.tv_sec || res.tv_sec < rhs.tv_sec)
res.tv_sec = TIME_T_MAX;
return res;
}
/*
* Add two timespec64 values and do a safety check for overflow.
* It's assumed that both values are valid (>= 0).

View File

@@ -1306,33 +1306,31 @@ EXPORT_SYMBOL(do_settimeofday64);
*
* Adds or subtracts an offset value from the current time.
*/
int timekeeping_inject_offset(struct timespec *ts)
static int timekeeping_inject_offset(struct timespec64 *ts)
{
struct timekeeper *tk = &tk_core.timekeeper;
unsigned long flags;
struct timespec64 ts64, tmp;
struct timespec64 tmp;
int ret = 0;
if (!timespec_inject_offset_valid(ts))
if (ts->tv_nsec < 0 || ts->tv_nsec >= NSEC_PER_SEC)
return -EINVAL;
ts64 = timespec_to_timespec64(*ts);
raw_spin_lock_irqsave(&timekeeper_lock, flags);
write_seqcount_begin(&tk_core.seq);
timekeeping_forward_now(tk);
/* Make sure the proposed value is valid */
tmp = timespec64_add(tk_xtime(tk), ts64);
if (timespec64_compare(&tk->wall_to_monotonic, &ts64) > 0 ||
tmp = timespec64_add(tk_xtime(tk), *ts);
if (timespec64_compare(&tk->wall_to_monotonic, ts) > 0 ||
!timespec64_valid_strict(&tmp)) {
ret = -EINVAL;
goto error;
}
tk_xtime_add(tk, &ts64);
tk_set_wall_to_mono(tk, timespec64_sub(tk->wall_to_monotonic, ts64));
tk_xtime_add(tk, ts);
tk_set_wall_to_mono(tk, timespec64_sub(tk->wall_to_monotonic, *ts));
error: /* even if we error out, we forwarded the time, so call update */
timekeeping_update(tk, TK_CLEAR_NTP | TK_MIRROR | TK_CLOCK_WAS_SET);
@@ -1345,7 +1343,40 @@ error: /* even if we error out, we forwarded the time, so call update */
return ret;
}
EXPORT_SYMBOL(timekeeping_inject_offset);
/*
* Indicates if there is an offset between the system clock and the hardware
* clock/persistent clock/rtc.
*/
int persistent_clock_is_local;
/*
* Adjust the time obtained from the CMOS to be UTC time instead of
* local time.
*
* This is ugly, but preferable to the alternatives. Otherwise we
* would either need to write a program to do it in /etc/rc (and risk
* confusion if the program gets run more than once; it would also be
* hard to make the program warp the clock precisely n hours) or
* compile in the timezone information into the kernel. Bad, bad....
*
* - TYT, 1992-01-01
*
* The best thing to do is to keep the CMOS clock in universal time (UTC)
* as real UNIX machines always do it. This avoids all headaches about
* daylight saving times and warping kernel clocks.
*/
void timekeeping_warp_clock(void)
{
if (sys_tz.tz_minuteswest != 0) {
struct timespec64 adjust;
persistent_clock_is_local = 1;
adjust.tv_sec = sys_tz.tz_minuteswest * 60;
adjust.tv_nsec = 0;
timekeeping_inject_offset(&adjust);
}
}
/**
* __timekeeping_set_tai_offset - Sets the TAI offset from UTC and monotonic
@@ -2289,6 +2320,72 @@ ktime_t ktime_get_update_offsets_now(unsigned int *cwsseq, ktime_t *offs_real,
return base;
}
/**
* timekeeping_validate_timex - Ensures the timex is ok for use in do_adjtimex
*/
static int timekeeping_validate_timex(struct timex *txc)
{
if (txc->modes & ADJ_ADJTIME) {
/* singleshot must not be used with any other mode bits */
if (!(txc->modes & ADJ_OFFSET_SINGLESHOT))
return -EINVAL;
if (!(txc->modes & ADJ_OFFSET_READONLY) &&
!capable(CAP_SYS_TIME))
return -EPERM;
} else {
/* In order to modify anything, you gotta be super-user! */
if (txc->modes && !capable(CAP_SYS_TIME))
return -EPERM;
/*
* if the quartz is off by more than 10% then
* something is VERY wrong!
*/
if (txc->modes & ADJ_TICK &&
(txc->tick < 900000/USER_HZ ||
txc->tick > 1100000/USER_HZ))
return -EINVAL;
}
if (txc->modes & ADJ_SETOFFSET) {
/* In order to inject time, you gotta be super-user! */
if (!capable(CAP_SYS_TIME))
return -EPERM;
/*
* Validate if a timespec/timeval used to inject a time
* offset is valid. Offsets can be postive or negative, so
* we don't check tv_sec. The value of the timeval/timespec
* is the sum of its fields,but *NOTE*:
* The field tv_usec/tv_nsec must always be non-negative and
* we can't have more nanoseconds/microseconds than a second.
*/
if (txc->time.tv_usec < 0)
return -EINVAL;
if (txc->modes & ADJ_NANO) {
if (txc->time.tv_usec >= NSEC_PER_SEC)
return -EINVAL;
} else {
if (txc->time.tv_usec >= USEC_PER_SEC)
return -EINVAL;
}
}
/*
* Check for potential multiplication overflows that can
* only happen on 64-bit systems:
*/
if ((txc->modes & ADJ_FREQUENCY) && (BITS_PER_LONG == 64)) {
if (LLONG_MIN / PPM_SCALE > txc->freq)
return -EINVAL;
if (LLONG_MAX / PPM_SCALE < txc->freq)
return -EINVAL;
}
return 0;
}
/**
* do_adjtimex() - Accessor function to NTP __do_adjtimex function
*/
@@ -2301,12 +2398,12 @@ int do_adjtimex(struct timex *txc)
int ret;
/* Validate the data before disabling interrupts */
ret = ntp_validate_timex(txc);
ret = timekeeping_validate_timex(txc);
if (ret)
return ret;
if (txc->modes & ADJ_SETOFFSET) {
struct timespec delta;
struct timespec64 delta;
delta.tv_sec = txc->time.tv_sec;
delta.tv_nsec = txc->time.tv_usec;
if (!(txc->modes & ADJ_NANO))

View File

@@ -10,7 +10,7 @@ extern ktime_t ktime_get_update_offsets_now(unsigned int *cwsseq,
extern int timekeeping_valid_for_hres(void);
extern u64 timekeeping_max_deferment(void);
extern int timekeeping_inject_offset(struct timespec *ts);
extern void timekeeping_warp_clock(void);
extern int timekeeping_suspend(void);
extern void timekeeping_resume(void);