Index: sys/kern/kern_tc.c =================================================================== --- sys/kern/kern_tc.c +++ sys/kern/kern_tc.c @@ -1245,7 +1245,7 @@ * when we booted. */ void -tc_setclock(struct timespec *ts) +tc_setclock(struct timespec *ts, bool abs) { struct timespec tbef, taft; struct bintime bt, bt2; @@ -1254,8 +1254,10 @@ nanotime(&tbef); mtx_lock_spin(&tc_setclock_mtx); cpu_tick_calibrate(1); - binuptime(&bt2); - bintime_sub(&bt, &bt2); + if (abs) { + binuptime(&bt2); + bintime_sub(&bt, &bt2); + } /* XXX fiddle all the little crinkly bits around the fiords... */ tc_windup(&bt); Index: sys/kern/kern_time.c =================================================================== --- sys/kern/kern_time.c +++ sys/kern/kern_time.c @@ -86,6 +86,7 @@ */ static int settime(struct thread *, struct timeval *); +static int steptime(struct thread *, struct timeval *, bool abs); static void timevalfix(struct timeval *); static int user_clock_nanosleep(struct thread *td, clockid_t clock_id, int flags, const struct timespec *ua_rqtp, @@ -120,7 +121,6 @@ { struct timeval delta, tv1, tv2; static struct timeval maxtime, laststep; - struct timespec ts; microtime(&tv1); delta = *tv; @@ -161,9 +161,17 @@ } } + return (steptime(td, tv, true)); +} + +static int +steptime(struct thread *td, struct timeval *tv, bool abs) +{ + struct timespec ts; + ts.tv_sec = tv->tv_sec; ts.tv_nsec = tv->tv_usec * 1000; - tc_setclock(&ts); + tc_setclock(&ts, abs); resettodr(); return (0); } @@ -405,18 +413,29 @@ if ((error = priv_check(td, PRIV_CLOCK_SETTIME)) != 0) return (error); - if (clock_id != CLOCK_REALTIME) - return (EINVAL); - if (ats->tv_nsec < 0 || ats->tv_nsec >= 1000000000 || - ats->tv_sec < 0) - return (EINVAL); - if (!allow_insane_settime && - (ats->tv_sec > 8000ULL * 365 * 24 * 60 * 60 || - ats->tv_sec < utc_offset())) - return (EINVAL); - /* XXX Don't convert nsec->usec and back */ - TIMESPEC_TO_TIMEVAL(&atv, ats); - error = settime(td, &atv); + error = EINVAL; + switch (clock_id) { + case CLOCK_ADJUST_REALTIME: + if (ats->tv_nsec < 0 || ats->tv_nsec >= 1000000000 || + (!allow_insane_settime && (ats->tv_sec > 3600 || + ats->tv_sec < -3600))) + break; + TIMESPEC_TO_TIMEVAL(&atv, ats); + error = steptime(td, &atv, false); + break; + case CLOCK_REALTIME: + if (ats->tv_nsec < 0 || ats->tv_nsec >= 1000000000 || + ats->tv_sec < 0) + break; + if (!allow_insane_settime && + (ats->tv_sec > 8000ULL * 365 * 24 * 60 * 60 || + ats->tv_sec < utc_offset())) + error = EINVAL; + /* XXX Don't convert nsec->usec and back */ + TIMESPEC_TO_TIMEVAL(&atv, ats); + error = settime(td, &atv); + break; + } return (error); } Index: sys/kern/subr_rtc.c =================================================================== --- sys/kern/subr_rtc.c +++ sys/kern/subr_rtc.c @@ -361,7 +361,7 @@ } if (ts.tv_sec >= 0) { - tc_setclock(&ts); + tc_setclock(&ts, true); #ifdef FFCLOCK ffclock_reset_clock(&ts); #endif Index: sys/sys/time.h =================================================================== --- sys/sys/time.h +++ sys/sys/time.h @@ -480,6 +480,7 @@ #define CLOCK_SECOND 13 /* FreeBSD-specific. */ #define CLOCK_THREAD_CPUTIME_ID 14 #define CLOCK_PROCESS_CPUTIME_ID 15 +#define CLOCK_ADJUST_REALTIME 16 /* FreeBSD-specific. */ #endif #ifndef TIMER_ABSTIME Index: sys/sys/timetc.h =================================================================== --- sys/sys/timetc.h +++ sys/sys/timetc.h @@ -88,7 +88,7 @@ u_int64_t tc_getfrequency(void); void tc_init(struct timecounter *tc); -void tc_setclock(struct timespec *ts); +void tc_setclock(struct timespec *ts, bool abs); void tc_ticktock(int cnt); void cpu_tick_calibration(void);