Improve the implementation of adjtime(2).

Apply the change as a continuous slew rather than as a series of
discrete steps and make it possible to adjust arbitraryly huge
amounts of time in either direction.

In practice this is done by hooking into the same once-per-second
loop as the NTP PLL and setting a suitable frequency offset deducting
the amount slewed from the remainder.  If the remaining delta is
larger than 1 second we slew at 5000PPM (5msec/sec), for a delta
less than a second we slew at 500PPM (500usec/sec) and for the last
one second period we will slew at whatever rate (less than 500PPM)
it takes to eliminate the delta entirely.

The old implementation stepped the clock a number of microseconds
every HZ to acheive the same effect, using the same rates of change.

Eliminate the global variables tickadj, tickdelta and timedelta and
their various use and initializations.

This removes the most significant obstacle to running timecounter and
NTP housekeeping from a timeout rather than hardclock.
This commit is contained in:
Poul-Henning Kamp 2002-04-15 12:23:11 +00:00
parent 52a3cde55d
commit e1d970f181
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=94754
5 changed files with 78 additions and 96 deletions

View File

@ -152,6 +152,8 @@ static l_fp time_offset; /* time offset (ns) */
static l_fp time_freq; /* frequency offset (ns/s) */
static l_fp time_adj; /* tick adjust (ns/s) */
static int64_t time_adjtime; /* correction from adjtime(2) (usec) */
#ifdef PPS_SYNC
/*
* The following variables are used when a pulse-per-second (PPS) signal
@ -437,6 +439,7 @@ void
ntp_update_second(struct timecounter *tcp)
{
u_int32_t *newsec;
int tickrate;
l_fp ftemp; /* 32/64-bit temporary */
newsec = &tcp->tc_offset.sec;
@ -532,7 +535,31 @@ ntp_update_second(struct timecounter *tcp)
time_adj = ftemp;
L_SUB(time_offset, ftemp);
L_ADD(time_adj, time_freq);
/*
* Apply any correction from adjtime(2). If more than one second
* off we slew at a rate of 5ms/s (5000 PPM) else 500us/s (500PPM)
* until the last second is slewed the final < 500 usecs.
*/
if (time_adjtime != 0) {
if (time_adjtime > 1000000)
tickrate = 5000;
else if (time_adjtime < -1000000)
tickrate = -5000;
else if (time_adjtime > 500)
tickrate = 500;
else if (time_adjtime < -500)
tickrate = -500;
else if (time_adjtime != 0)
tickrate = time_adjtime;
else
tickrate = 0; /* GCC sucks! */
time_adjtime -= tickrate;
L_LINT(ftemp, tickrate * 1000);
L_ADD(time_adj, ftemp);
}
tcp->tc_adjustment = time_adj;
#ifdef PPS_SYNC
if (pps_valid > 0)
pps_valid--;
@ -865,3 +892,50 @@ hardpps(tsp, nsec)
time_freq = pps_freq;
}
#endif /* PPS_SYNC */
#ifndef _SYS_SYSPROTO_H_
struct adjtime_args {
struct timeval *delta;
struct timeval *olddelta;
};
#endif
/*
* MPSAFE
*/
/* ARGSUSED */
int
adjtime(struct thread *td, struct adjtime_args *uap)
{
struct timeval atv;
int error;
mtx_lock(&Giant);
if ((error = suser(td)))
goto done2;
if (uap->olddelta) {
atv.tv_sec = time_adjtime / 1000000;
atv.tv_usec = time_adjtime % 1000000;
if (atv.tv_usec < 0) {
atv.tv_usec += 1000000;
atv.tv_sec--;
}
printf("Old: time_adjtime = %ld.%06ld %lld\n",
atv.tv_sec, atv.tv_usec, time_adjtime);
error = copyout(&atv, uap->olddelta, sizeof(atv));
if (error)
goto done2;
}
if (uap->delta) {
error = copyin(uap->delta, &atv, sizeof(atv));
if (error)
goto done2;
time_adjtime = (int64_t)atv.tv_sec * 1000000 + atv.tv_usec;
printf("New: time_adjtime = %ld.%06ld %lld\n",
atv.tv_sec, atv.tv_usec, time_adjtime);
}
done2:
mtx_unlock(&Giant);
return (error);
}

View File

@ -238,11 +238,11 @@ tco_setscales(struct timecounter *tc)
/*
* We get nanoseconds with 32 bit binary fraction and want
* 64 bit binary fraction: x = a * 2^32 / 10^9 = a * 4.294967296
* The range is +/- 500PPM so we can only multiply by about 8500
* without overflowing. The best suitable fraction is 4398/1024.
* Divide by 2 times 1024 to match the temporary lower precision.
* The range is +/- 5000PPM so we can only multiply by about 850
* without overflowing. The best suitable fraction is 2199/512.
* Divide by 2 times 512 to match the temporary lower precision.
*/
scale += (tc->tc_adjustment * 4398) / 2048;
scale += (tc->tc_adjustment / 1024) * 2199;
scale /= tc->tc_tweak->tc_frequency;
tc->tc_scale = scale * 2;
}
@ -338,7 +338,6 @@ tc_windup(void)
{
struct timecounter *tc, *tco;
struct bintime bt;
struct timeval tvt;
unsigned ogen, delta;
int i;
@ -362,20 +361,6 @@ tc_windup(void)
*/
if (tco->tc_poll_pps)
tco->tc_poll_pps(tco);
if (timedelta != 0) {
tvt = boottime;
tvt.tv_usec += tickdelta;
if (tvt.tv_usec >= 1000000) {
tvt.tv_sec++;
tvt.tv_usec -= 1000000;
} else if (tvt.tv_usec < 0) {
tvt.tv_sec--;
tvt.tv_usec += 1000000;
}
boottime = tvt;
timeval2bintime(&boottime, &boottimebin);
timedelta -= tickdelta;
}
for (i = tc->tc_offset.sec - tco->tc_offset.sec; i > 0; i--) {
ntp_update_second(tc); /* XXX only needed if xntpd runs */
tco_setscales(tc);

View File

@ -386,78 +386,6 @@ settimeofday(td, uap)
}
return (error);
}
int tickdelta; /* current clock skew, us. per tick */
long timedelta; /* unapplied time correction, us. */
static long bigadj = 1000000; /* use 10x skew above bigadj us. */
#ifndef _SYS_SYSPROTO_H_
struct adjtime_args {
struct timeval *delta;
struct timeval *olddelta;
};
#endif
/*
* MPSAFE
*/
/* ARGSUSED */
int
adjtime(td, uap)
struct thread *td;
register struct adjtime_args *uap;
{
struct timeval atv;
register long ndelta, ntickdelta, odelta;
int s, error;
mtx_lock(&Giant);
if ((error = suser(td)))
goto done2;
error = copyin((caddr_t)uap->delta, (caddr_t)&atv,
sizeof(struct timeval));
if (error)
goto done2;
/*
* Compute the total correction and the rate at which to apply it.
* Round the adjustment down to a whole multiple of the per-tick
* delta, so that after some number of incremental changes in
* hardclock(), tickdelta will become zero, lest the correction
* overshoot and start taking us away from the desired final time.
*/
ndelta = atv.tv_sec * 1000000 + atv.tv_usec;
if (ndelta > bigadj || ndelta < -bigadj)
ntickdelta = 10 * tickadj;
else
ntickdelta = tickadj;
if (ndelta % ntickdelta)
ndelta = ndelta / ntickdelta * ntickdelta;
/*
* To make hardclock()'s job easier, make the per-tick delta negative
* if we want time to run slower; then hardclock can simply compute
* tick + tickdelta, and subtract tickdelta from timedelta.
*/
if (ndelta < 0)
ntickdelta = -ntickdelta;
s = splclock();
odelta = timedelta;
timedelta = ndelta;
tickdelta = ntickdelta;
splx(s);
if (uap->olddelta) {
atv.tv_sec = odelta / 1000000;
atv.tv_usec = odelta % 1000000;
(void) copyout((caddr_t)&atv, (caddr_t)uap->olddelta,
sizeof(struct timeval));
}
done2:
mtx_unlock(&Giant);
return (error);
}
/*
* Get value of an interval timer. The process virtual and
* profiling virtual time timers are kept in the p_stats area, since

View File

@ -65,7 +65,6 @@
int hz;
int tick;
int tickadj; /* can adjust 30ms in 60s */
int maxusers; /* base tunable */
int maxproc; /* maximum # of processes */
int maxprocperuid; /* max # of procs per user */
@ -100,7 +99,6 @@ init_param1(void)
hz = HZ;
TUNABLE_INT_FETCH("kern.hz", &hz);
tick = 1000000 / hz;
tickadj = howmany(30000, 60 * hz); /* can adjust 30ms in 60s */
#ifdef VM_SWZONE_SIZE_MAX
maxswzone = VM_SWZONE_SIZE_MAX;

View File

@ -71,15 +71,12 @@ extern struct timeval boottime;
extern struct timezone tz; /* XXX */
extern int tick; /* usec per tick (1000000 / hz) */
extern int tickadj; /* "standard" clock skew, us./tick */
extern int hz; /* system clock's frequency */
extern int psratio; /* ratio: prof / stat */
extern int stathz; /* statistics clock's frequency */
extern int profhz; /* profiling clock's frequency */
extern int ticks;
extern int lbolt; /* once a second sleep address */
extern int tickdelta;
extern long timedelta;
#endif /* _KERNEL */