@@ -157,6 +157,8 @@ extern void do_gettimeofday(struct timeval *tv);
extern int do_settimeofday(const struct timespec *tv);
extern int do_sys_settimeofday(const struct timespec *tv,
const struct timezone *tz);
+extern int do_first_settimeofday(const struct timespec *ts);
+
#define do_posix_clock_monotonic_gettime(ts) ktime_get_ts(ts)
extern long do_utimes(int dfd, const char __user *filename, struct timespec *times, int flags);
struct itimerval;
@@ -487,6 +487,25 @@ void do_gettimeofday(struct timeval *tv)
}
EXPORT_SYMBOL(do_gettimeofday);
+
+static void __do_settimeofday(struct timekeeper *tk, const struct timespec *tv)
+{
+ struct timespec ts_delta, xt;
+
+ timekeeping_forward_now(tk);
+
+ xt = tk_xtime(tk);
+ ts_delta.tv_sec = tv->tv_sec - xt.tv_sec;
+ ts_delta.tv_nsec = tv->tv_nsec - xt.tv_nsec;
+
+ tk_set_wall_to_mono(tk, timespec_sub(tk->wall_to_monotonic, ts_delta));
+
+ tk_set_xtime(tk, tv);
+
+ timekeeping_update(tk, TK_CLEAR_NTP | TK_MIRROR | TK_CLOCK_WAS_SET);
+}
+
+
/**
* do_settimeofday - Sets the time of day
* @tv: pointer to the timespec variable containing the new time
@@ -496,7 +515,6 @@ EXPORT_SYMBOL(do_gettimeofday);
int do_settimeofday(const struct timespec *tv)
{
struct timekeeper *tk = &timekeeper;
- struct timespec ts_delta, xt;
unsigned long flags;
if (!timespec_valid_strict(tv))
@@ -505,27 +523,43 @@ int do_settimeofday(const struct timespec *tv)
raw_spin_lock_irqsave(&timekeeper_lock, flags);
write_seqcount_begin(&timekeeper_seq);
- timekeeping_forward_now(tk);
+ __do_settimeofday(tk, tv);
- xt = tk_xtime(tk);
- ts_delta.tv_sec = tv->tv_sec - xt.tv_sec;
- ts_delta.tv_nsec = tv->tv_nsec - xt.tv_nsec;
+ write_seqcount_end(&timekeeper_seq);
+ raw_spin_unlock_irqrestore(&timekeeper_lock, flags);
- tk_set_wall_to_mono(tk, timespec_sub(tk->wall_to_monotonic, ts_delta));
+ /* signal hrtimers about time change */
+ clock_was_set();
- tk_set_xtime(tk, tv);
+ return 0;
+}
+EXPORT_SYMBOL(do_settimeofday);
- timekeeping_update(tk, TK_CLEAR_NTP | TK_MIRROR | TK_CLOCK_WAS_SET);
+int do_first_settimeofday(const struct timespec *tv)
+{
+ struct timekeeper *tk = &timekeeper;
+ unsigned long flags;
+ int ret = 0;
+
+ if (!timespec_valid_strict(tv))
+ return -EINVAL;
+
+ raw_spin_lock_irqsave(&timekeeper_lock, flags);
+ write_seqcount_begin(&timekeeper_seq);
+
+ if (!tk->wall_to_monotonic.tv_sec && !tk->wall_to_monotonic.tv_nsec)
+ __do_settimeofday(tk, tv);
+ else
+ ret = -EACCES;
write_seqcount_end(&timekeeper_seq);
raw_spin_unlock_irqrestore(&timekeeper_lock, flags);
/* signal hrtimers about time change */
clock_was_set();
- return 0;
+ return ret;
}
-EXPORT_SYMBOL(do_settimeofday);
/**
* timekeeping_inject_offset - Adds or subtracts from the current time.
There's some cases in the kernel, particuarly with RTC drivers, where we want to set the time, but only if no one else has already set it. This is useful for systems where the RTC driver is a module, and is loaded late in initialization. However, we want to be sure we don't override the time that may have been set by userspace. Cc: John Whitmore <arigead@gmail.com> Cc: Alessandro Zummo <a.zummo@towertech.it> Cc: Alexander Holler <holler@ahsoftware.de> Signed-off-by: John Stultz <john.stultz@linaro.org> --- include/linux/time.h | 2 ++ kernel/time/timekeeping.c | 54 ++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 46 insertions(+), 10 deletions(-)