kernel clock fix for NTP

Ulrich Windl (Ulrich.Windl@rz.uni-regensburg.de)
Thu, 25 Apr 1996 08:48:28 +0200


I was wondering why xntp could not synchronize the clock as well as I
know from other platforms (story reported). Looking around in the
kernel code, and doing some tests, I've found out that the current
kernel only allows a frequency correction up to 100ppm, whereas Dave
Mills stated that the code can handle "well over 350ppm". In the
example implementation 512ppm are allowed, and I merged the changes
into timex.h. I also changed some magic constants to symbolic ones,
as in the reference implementation.

Instead of widening the range of the frequency correction, on could
also adjust the kernel's "tick" value, but that is rather crude
(Using 10001 is 100ppm faster). BTW. My clock drifts at about
110ppm.

Some added #ifndefs reduce the compiler warnings (CONFIG_APM with
pentium). Some other lines were simply joined, because the result is
short enough.

This should not break anything.
As my editor will surely wrap long lines, I'll add the same thing
uuencoded...

Ulrich

===================================================================
RCS file: ./kernel/sched.c,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -r1.1 -r1.2
--- sched.c 1996/04/16 19:19:17 1.1
+++ sched.c 1996/04/22 21:27:03 1.2
@@ -2,6 +2,8 @@
* linux/kernel/sched.c
*
* Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * 1996-04-21 Modified by Ulrich Windl to make NTP work
*/

/*
@@ -52,14 +54,15 @@
/*
* phase-lock loop variables
*/
-int time_state = TIME_BAD; /* clock synchronization status */
-int time_status = STA_UNSYNC | STA_PLL; /* clock status bits */
+/* TIME_ERROR prevents overwriting the CMOS clock */
+int time_state = TIME_ERROR; /* clock synchronization status */
+int time_status = STA_UNSYNC; /* clock status bits */
long time_offset = 0; /* time adjustment (us) */
long time_constant = 2; /* pll time constant */
long time_tolerance = MAXFREQ; /* frequency tolerance (ppm) */
long time_precision = 1; /* clock precision (us) */
-long time_maxerror = 0x70000000;/* maximum error */
-long time_esterror = 0x70000000;/* estimated error */
+long time_maxerror = MAXPHASE; /* maximum error (us) */
+long time_esterror = MAXPHASE; /* estimated error (us) */
long time_phase = 0; /* phase offset (scaled us) */
long time_freq = 0; /* frequency offset (scaled ppm) */
long time_adj = 0; /* tick adjust (scaled 1 / HZ) */
@@ -689,9 +692,9 @@
long ltemp;

/* Bump the maxerror field */
- time_maxerror = (0x70000000-time_maxerror <
- time_tolerance >> SHIFT_USEC) ?
- 0x70000000 : (time_maxerror + (time_tolerance >> SHIFT_USEC));
+ time_maxerror += time_tolerance >> SHIFT_USEC;
+ if ( time_maxerror > MAXPHASE )
+ time_maxerror = MAXPHASE;

/*
* Leap second processing. If in leap-insert state at
@@ -728,7 +731,6 @@
break;

case TIME_OOP:
-
time_state = TIME_WAIT;
break;

@@ -751,21 +753,17 @@
if (!(time_status & STA_FLL))
ltemp >>= SHIFT_KG + time_constant;
if (ltemp > (MAXPHASE / MINSEC) << SHIFT_UPDATE)
- ltemp = (MAXPHASE / MINSEC) <<
- SHIFT_UPDATE;
+ ltemp = (MAXPHASE / MINSEC) << SHIFT_UPDATE;
time_offset += ltemp;
- time_adj = -ltemp << (SHIFT_SCALE - SHIFT_HZ -
- SHIFT_UPDATE);
+ time_adj = -ltemp << (SHIFT_SCALE - SHIFT_HZ - SHIFT_UPDATE);
} else {
ltemp = time_offset;
if (!(time_status & STA_FLL))
ltemp >>= SHIFT_KG + time_constant;
if (ltemp > (MAXPHASE / MINSEC) << SHIFT_UPDATE)
- ltemp = (MAXPHASE / MINSEC) <<
- SHIFT_UPDATE;
+ ltemp = (MAXPHASE / MINSEC) << SHIFT_UPDATE;
time_offset -= ltemp;
- time_adj = ltemp << (SHIFT_SCALE - SHIFT_HZ -
- SHIFT_UPDATE);
+ time_adj = ltemp << (SHIFT_SCALE - SHIFT_HZ - SHIFT_UPDATE);
}

/*
@@ -799,6 +797,7 @@
#endif
}

+/* in the NTP reference this is called "hardclock()" */
static void update_wall_time_one_tick(void)
{
/*
===================================================================
RCS file: ./kernel/time.c,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -r1.1 -r1.2
--- time.c 1996/02/17 07:19:43 1.1
+++ time.c 1996/04/22 21:29:57 1.2
@@ -75,9 +75,9 @@
cli();
xtime.tv_sec = value;
xtime.tv_usec = 0;
- time_state = TIME_BAD;
- time_maxerror = 0x70000000;
- time_esterror = 0x70000000;
+ time_state = TIME_ERROR;
+ time_maxerror = MAXPHASE;
+ time_esterror = MAXPHASE;
sti();
return 0;
}
@@ -317,7 +317,7 @@
time_freq = time_tolerance;
else if (time_freq < -time_tolerance)
time_freq = -time_tolerance;
- }
+ } /* STA_PLL || STA_PPSTIME */
if (txc.modes & ADJ_TICK)
tick = txc.tick;

===================================================================
RCS file: ./include/linux/timex.h,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -r1.1 -r1.2
--- timex.h 1996/04/16 19:19:16 1.1
+++ timex.h 1996/04/24 20:11:50 1.2
@@ -17,6 +17,21 @@
/*
* Modification history timex.h
*
+ * 26 Sep 94 David L. Mills
+ * Added defines for hybrid phase/frequency-lock loop.
+ *
+ * 19 Mar 94 David L. Mills
+ * Moved defines from kernel routines to header file and added new
+ * defines for PPS phase-lock loop.
+ *
+ * 20 Feb 94 David L. Mills
+ * Revised status codes and structures for external clock and PPS
+ * signal discipline.
+ *
+ * 28 Nov 93 David L. Mills
+ * Adjusted parameters to improve stability and increase poll
+ * interval.
+ *
* 17 Sep 93 David L. Mills
* Created file $NTP/include/sys/timex.h
* 07 Oct 93 Torsten Duwe
@@ -67,14 +82,16 @@
* SHIFT_USEC defines the scaling (shift) of the time_freq and
* time_tolerance variables, which represent the current frequency
* offset and maximum frequency tolerance.
+ *
+ * FINEUSEC is 1 us in SHIFT_UPDATE units of the time_phase
variable.
*/
-#define SHIFT_SCALE 22 /* shift for phase scale factor */
-#define SHIFT_UPDATE (SHIFT_KG + MAXTC) /* shift for offset scale
factor */
+#define SHIFT_SCALE 22 /* phase scale (shift) */
+#define SHIFT_UPDATE (SHIFT_KG + MAXTC) /* time offset scale (shift)
*/
#define SHIFT_USEC 16 /* frequency offset scale (shift) */
#define FINEUSEC (1L << SHIFT_SCALE) /* 1 us in phase units */

#define MAXPHASE 512000L /* max phase error (us) */
-#define MAXFREQ (100L << SHIFT_USEC) /* max frequency error (ppm)
*/
+#define MAXFREQ (512L << SHIFT_USEC) /* max frequency error (ppm)
*/
#define MAXTIME (200L << PPS_AVG) /* max PPS error (jitter) (200 us)
*/
#define MINSEC 16L /* min interval between updates (s)
*/
#define MAXSEC 1200L /* max interval between updates (s)
*/
@@ -171,6 +188,9 @@
#define MOD_ESTERROR ADJ_ESTERROR
#define MOD_STATUS ADJ_STATUS
#define MOD_TIMECONST ADJ_TIMECONST
+#define MOD_CLKB ADJ_TICK
+#define MOD_CLKA ADJ_OFFSET_SINGLESHOT /* 0x8000 in original */
+

/*
* Status codes (timex.status)
@@ -198,17 +218,19 @@
/*
* Clock states (time_state)
*/
-#define TIME_OK 0 /* clock synchronized */
+#define TIME_OK 0 /* clock synchronized, no leap second */
#define TIME_INS 1 /* insert leap second */
#define TIME_DEL 2 /* delete leap second */
#define TIME_OOP 3 /* leap second in progress */
-#define TIME_WAIT 4 /* leap second has occurred */
+#define TIME_WAIT 4 /* leap second has occured */
#define TIME_ERROR 5 /* clock not synchronized */
#define TIME_BAD TIME_ERROR /* bw compat */

#ifdef __KERNEL__
/*
* kernel variables
+ * Note: maximum error = NTP synch distance = dispersion + delay /
2;
+ * estimated error = NTP dispersion.
*/
extern long tick; /* timer interrupt period */
extern int tickadj; /* amount of adjustment per tick */
===================================================================
RCS file: ./arch/i386/kernel/time.c,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -r1.1 -r1.2
--- time.c 1996/03/24 14:45:23 1.1
+++ time.c 1996/04/22 21:12:19 1.2
@@ -29,6 +29,7 @@

extern int setup_x86_irq(int, struct irqaction *);

+#ifndef CONFIG_APM /* cycle counter may be unreliable */
/* Cycle counter value at the previous timer interrupt.. */
static unsigned long long last_timer_cc = 0;
static unsigned long long init_timer_cc = 0;
@@ -82,6 +83,7 @@
quotient = 1000000/HZ-1;
return quotient;
}
+#endif

/* This function must be called with interrupts disabled
* It was inspired by Steve McCanne's microtime-i386 for BSD. --
jrs
@@ -271,6 +273,7 @@

}

+#ifndef CONFIG_APM /* cycle counter may be unreliable */
/*
* This is the same as the above, except we _also_ save the current
* cycle counter value at the time of the timer interrupt, so that
@@ -284,6 +287,7 @@
"=d" (((unsigned long *) &last_timer_cc)[1]));
timer_interrupt(irq, NULL, regs);
}
+#endif

/* Converts Gregorian date to seconds since 1970-01-01 00:00:00.
* Assumes input in normal date format, i.e. 1980-12-31 23:59:59
------------
Ulrich Windl Klinikum der Universitaet Regensburg
Rechenzentrum DV-med Franz-Josef-Strauss-Allee 11
Tel: +49 941 944-5879 D-93053 Regensburg
FAX: +49 941 944-5882
"The bad PC memory subsystems are really bad." (Linus Torvalds, 19.04.1996)
"(...) and this is under Blowlaris/StUNOS" (David S. Miller, 23.04.1996)