Commit a05d0bae authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] More time clean up stuff

From: george anzinger <george@mvista.com>

This patch addresses issues of roundoff error in the time keeping and NTP
code as follows:

The conversion of "actual jiffies" to TICK_USEC and then to TICK_NSEC
introduced large errors if jiffies was not a power of 10 (e.g.  1024 for
the ia64).  Most of this is avoided by converting directly to TICK_NSEC.

The calculation of MAX_SEC_IN_JIFFIES (the largest timespec or timeval the
kernel will attempt) had overflow problems in the 64-bit machines.  We
introduce a different equation for those machines.

The NTP frequency update code was allowing a micro second of error to
accumulate before applying the correction.  We change FINEUSEC to FINENSEC
to do the correction as soon as a full nanosecond has accumulated.

The initial calculation of time_freq for NTP had severe roundoff errors for
HZ not a power of 10 (i.e.  1024).  A new equation fixes this.

clock_nanosleep is changed to round up to the next jiffie to cover starting
between jiffies.
parent b9cebc5d
...@@ -127,7 +127,7 @@ int do_settimeofday(struct timespec *tv) ...@@ -127,7 +127,7 @@ int do_settimeofday(struct timespec *tv)
* made, and then undo it! * made, and then undo it!
*/ */
tv->tv_nsec -= timer->get_offset() * NSEC_PER_USEC; tv->tv_nsec -= timer->get_offset() * NSEC_PER_USEC;
tv->tv_nsec -= (jiffies - wall_jiffies) * TICK_NSEC(TICK_USEC); tv->tv_nsec -= (jiffies - wall_jiffies) * TICK_NSEC;
while (tv->tv_nsec < 0) { while (tv->tv_nsec < 0) {
tv->tv_nsec += NSEC_PER_SEC; tv->tv_nsec += NSEC_PER_SEC;
......
...@@ -26,7 +26,7 @@ static inline void arch_setup_pc9800(void) ...@@ -26,7 +26,7 @@ static inline void arch_setup_pc9800(void)
CLOCK_TICK_RATE = PC9800_8MHz_P() ? 1996800 : 2457600; CLOCK_TICK_RATE = PC9800_8MHz_P() ? 1996800 : 2457600;
printk(KERN_DEBUG "CLOCK_TICK_RATE = %d\n", CLOCK_TICK_RATE); printk(KERN_DEBUG "CLOCK_TICK_RATE = %d\n", CLOCK_TICK_RATE);
tick_usec = TICK_USEC; /* ACTHZ period (usec) */ tick_usec = TICK_USEC; /* ACTHZ period (usec) */
tick_nsec = TICK_NSEC(TICK_USEC); /* USER_HZ period (nsec) */ tick_nsec = TICK_NSEC; /* USER_HZ period (nsec) */
pc9800_misc_flags = PC9800_MISC_FLAGS; pc9800_misc_flags = PC9800_MISC_FLAGS;
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
......
...@@ -14,7 +14,11 @@ ...@@ -14,7 +14,11 @@
typedef unsigned long cycles_t; typedef unsigned long cycles_t;
#define CLOCK_TICK_RATE 100000000 /*
* Something low processor frequency like 100Mhz but
* yet multiple of HZ to avoid truncation in some formulas.
*/
#define CLOCK_TICK_RATE (HZ * 100000UL)
static inline cycles_t static inline cycles_t
get_cycles (void) get_cycles (void)
......
...@@ -69,10 +69,11 @@ struct timezone { ...@@ -69,10 +69,11 @@ struct timezone {
#ifndef NSEC_PER_USEC #ifndef NSEC_PER_USEC
#define NSEC_PER_USEC (1000L) #define NSEC_PER_USEC (1000L)
#endif #endif
/* /*
* We want to do realistic conversions of time so we need to use the same * We want to do realistic conversions of time so we need to use the same
* values the update wall clock code uses as the jiffie size. This value * values the update wall clock code uses as the jiffie size. This value
* is: TICK_NSEC(TICK_USEC) (both of which are defined in timex.h). This * is: TICK_NSEC (both of which are defined in timex.h). This
* is a constant and is in nanoseconds. We will used scaled math and * is a constant and is in nanoseconds. We will used scaled math and
* with a scales defined here as SEC_JIFFIE_SC, USEC_JIFFIE_SC and * with a scales defined here as SEC_JIFFIE_SC, USEC_JIFFIE_SC and
* NSEC_JIFFIE_SC. Note that these defines contain nothing but * NSEC_JIFFIE_SC. Note that these defines contain nothing but
...@@ -83,23 +84,30 @@ struct timezone { ...@@ -83,23 +84,30 @@ struct timezone {
#define NSEC_JIFFIE_SC (SEC_JIFFIE_SC + 30) #define NSEC_JIFFIE_SC (SEC_JIFFIE_SC + 30)
#define USEC_JIFFIE_SC (SEC_JIFFIE_SC + 20) #define USEC_JIFFIE_SC (SEC_JIFFIE_SC + 20)
#define SEC_CONVERSION ((unsigned long)(((u64)NSEC_PER_SEC << SEC_JIFFIE_SC) /\ #define SEC_CONVERSION ((unsigned long)(((u64)NSEC_PER_SEC << SEC_JIFFIE_SC) /\
(u64)TICK_NSEC(TICK_USEC))) (u64)TICK_NSEC))
#define NSEC_CONVERSION ((unsigned long)(((u64)1 << NSEC_JIFFIE_SC) / \ #define NSEC_CONVERSION ((unsigned long)(((u64)1 << NSEC_JIFFIE_SC) /\
(u64)TICK_NSEC(TICK_USEC))) (u64)TICK_NSEC))
#define USEC_CONVERSION \ #define USEC_CONVERSION ((unsigned long)(((u64)NSEC_PER_USEC << USEC_JIFFIE_SC)/\
((unsigned long)(((u64)NSEC_PER_USEC << USEC_JIFFIE_SC)/ \ (u64)TICK_NSEC))
(u64)TICK_NSEC(TICK_USEC))) #if BITS_PER_LONG < 64
#define MAX_SEC_IN_JIFFIES \ # define MAX_SEC_IN_JIFFIES \
(u32)((u64)((u64)MAX_JIFFY_OFFSET * TICK_NSEC(TICK_USEC)) / NSEC_PER_SEC) (long)((u64)((u64)MAX_JIFFY_OFFSET * TICK_NSEC) / NSEC_PER_SEC)
#else /* take care of overflow on 64 bits machines */
# define MAX_SEC_IN_JIFFIES \
(SH_DIV((MAX_JIFFY_OFFSET >> SEC_JIFFIE_SC) * TICK_NSEC, NSEC_PER_SEC, 1) - 1)
#endif
static __inline__ unsigned long static __inline__ unsigned long
timespec_to_jiffies(struct timespec *value) timespec_to_jiffies(struct timespec *value)
{ {
unsigned long sec = value->tv_sec; unsigned long sec = value->tv_sec;
long nsec = value->tv_nsec + TICK_NSEC(TICK_USEC) - 1; long nsec = value->tv_nsec + TICK_NSEC - 1;
if (sec >= MAX_SEC_IN_JIFFIES) if (sec >= MAX_SEC_IN_JIFFIES){
return MAX_JIFFY_OFFSET; sec = MAX_SEC_IN_JIFFIES;
nsec = 0;
}
return (((u64)sec * SEC_CONVERSION) + return (((u64)sec * SEC_CONVERSION) +
(((u64)nsec * NSEC_CONVERSION) >> (((u64)nsec * NSEC_CONVERSION) >>
(NSEC_JIFFIE_SC - SEC_JIFFIE_SC))) >> SEC_JIFFIE_SC; (NSEC_JIFFIE_SC - SEC_JIFFIE_SC))) >> SEC_JIFFIE_SC;
...@@ -113,7 +121,7 @@ jiffies_to_timespec(unsigned long jiffies, struct timespec *value) ...@@ -113,7 +121,7 @@ jiffies_to_timespec(unsigned long jiffies, struct timespec *value)
* Convert jiffies to nanoseconds and seperate with * Convert jiffies to nanoseconds and seperate with
* one divide. * one divide.
*/ */
u64 nsec = (u64)jiffies * TICK_NSEC(TICK_USEC); u64 nsec = (u64)jiffies * TICK_NSEC;
value->tv_sec = div_long_long_rem(nsec, NSEC_PER_SEC, &value->tv_nsec); value->tv_sec = div_long_long_rem(nsec, NSEC_PER_SEC, &value->tv_nsec);
} }
...@@ -122,10 +130,12 @@ static __inline__ unsigned long ...@@ -122,10 +130,12 @@ static __inline__ unsigned long
timeval_to_jiffies(struct timeval *value) timeval_to_jiffies(struct timeval *value)
{ {
unsigned long sec = value->tv_sec; unsigned long sec = value->tv_sec;
long usec = value->tv_usec + USEC_PER_SEC / HZ - 1; long usec = value->tv_usec + TICK_USEC - 1;
if (sec >= MAX_SEC_IN_JIFFIES) if (sec >= MAX_SEC_IN_JIFFIES){
return MAX_JIFFY_OFFSET; sec = MAX_SEC_IN_JIFFIES;
usec = 0;
}
return (((u64)sec * SEC_CONVERSION) + return (((u64)sec * SEC_CONVERSION) +
(((u64)usec * USEC_CONVERSION) >> (((u64)usec * USEC_CONVERSION) >>
(USEC_JIFFIE_SC - SEC_JIFFIE_SC))) >> SEC_JIFFIE_SC; (USEC_JIFFIE_SC - SEC_JIFFIE_SC))) >> SEC_JIFFIE_SC;
...@@ -138,7 +148,7 @@ jiffies_to_timeval(unsigned long jiffies, struct timeval *value) ...@@ -138,7 +148,7 @@ jiffies_to_timeval(unsigned long jiffies, struct timeval *value)
* Convert jiffies to nanoseconds and seperate with * Convert jiffies to nanoseconds and seperate with
* one divide. * one divide.
*/ */
u64 nsec = (u64)jiffies * TICK_NSEC(TICK_USEC); u64 nsec = (u64)jiffies * TICK_NSEC;
value->tv_sec = div_long_long_rem(nsec, NSEC_PER_SEC, &value->tv_usec); value->tv_sec = div_long_long_rem(nsec, NSEC_PER_SEC, &value->tv_usec);
value->tv_usec /= NSEC_PER_USEC; value->tv_usec /= NSEC_PER_USEC;
} }
......
...@@ -102,19 +102,19 @@ ...@@ -102,19 +102,19 @@
* variable which serves as an extension to the low-order bits of the * variable which serves as an extension to the low-order bits of the
* system clock variable. The SHIFT_UPDATE define establishes the decimal * system clock variable. The SHIFT_UPDATE define establishes the decimal
* point of the time_offset variable which represents the current offset * point of the time_offset variable which represents the current offset
* with respect to standard time. The FINEUSEC define represents 1 usec in * with respect to standard time. The FINENSEC define represents 1 nsec in
* scaled units. * scaled units.
* *
* SHIFT_USEC defines the scaling (shift) of the time_freq and * SHIFT_USEC defines the scaling (shift) of the time_freq and
* time_tolerance variables, which represent the current frequency * time_tolerance variables, which represent the current frequency
* offset and maximum frequency tolerance. * offset and maximum frequency tolerance.
* *
* FINEUSEC is 1 us in SHIFT_UPDATE units of the time_phase variable. * FINENSEC is 1 ns in SHIFT_UPDATE units of the time_phase variable.
*/ */
#define SHIFT_SCALE 22 /* phase scale (shift) */ #define SHIFT_SCALE 22 /* phase scale (shift) */
#define SHIFT_UPDATE (SHIFT_KG + MAXTC) /* time offset scale (shift) */ #define SHIFT_UPDATE (SHIFT_KG + MAXTC) /* time offset scale (shift) */
#define SHIFT_USEC 16 /* frequency offset scale (shift) */ #define SHIFT_USEC 16 /* frequency offset scale (shift) */
#define FINEUSEC (1L << SHIFT_SCALE) /* 1 us in phase units */ #define FINENSEC (1L << (SHIFT_SCALE - 10)) /* ~1 ns in phase units */
#define MAXPHASE 512000L /* max phase error (us) */ #define MAXPHASE 512000L /* max phase error (us) */
#define MAXFREQ (512L << SHIFT_USEC) /* max frequency error (ppm) */ #define MAXFREQ (512L << SHIFT_USEC) /* max frequency error (ppm) */
...@@ -162,7 +162,7 @@ ...@@ -162,7 +162,7 @@
* (NOM << LSH) / DEN * (NOM << LSH) / DEN
* This however means trouble for large NOM, because (NOM << LSH) may no * This however means trouble for large NOM, because (NOM << LSH) may no
* longer fit in 32 bits. The following way of calculating this gives us * longer fit in 32 bits. The following way of calculating this gives us
* some slack, under the following onditions: * some slack, under the following conditions:
* - (NOM / DEN) fits in (32 - LSH) bits. * - (NOM / DEN) fits in (32 - LSH) bits.
* - (NOM % DEN) fits in (32 - LSH) bits. * - (NOM % DEN) fits in (32 - LSH) bits.
*/ */
...@@ -172,12 +172,16 @@ ...@@ -172,12 +172,16 @@
/* HZ is the requested value. ACTHZ is actual HZ ("<< 8" is for accuracy) */ /* HZ is the requested value. ACTHZ is actual HZ ("<< 8" is for accuracy) */
#define ACTHZ (SH_DIV (CLOCK_TICK_RATE, LATCH, 8)) #define ACTHZ (SH_DIV (CLOCK_TICK_RATE, LATCH, 8))
/* TICK_NSEC is the time between ticks in nsec assuming real ACTHZ */
#define TICK_NSEC (SH_DIV (1000000UL * 1000, ACTHZ, 8))
/* TICK_USEC is the time between ticks in usec assuming fake USER_HZ */ /* TICK_USEC is the time between ticks in usec assuming fake USER_HZ */
#define TICK_USEC ((1000000UL + USER_HZ/2) / USER_HZ) #define TICK_USEC ((TICK_NSEC + 1000UL/2) / 1000UL)
/* TICK_NSEC is the time between ticks in nsec assuming real ACTHZ and */ /* TICK_USEC_TO_NSEC is the time between ticks in nsec assuming real ACTHZ and */
/* a value TUSEC for TICK_USEC (can be set bij adjtimex) */ /* a value TUSEC for TICK_USEC (can be set bij adjtimex) */
#define TICK_NSEC(TUSEC) (SH_DIV (TUSEC * USER_HZ * 1000, ACTHZ, 8)) #define TICK_USEC_TO_NSEC(TUSEC) (SH_DIV (TUSEC * USER_HZ * 1000, ACTHZ, 8))
#include <linux/time.h> #include <linux/time.h>
/* /*
......
...@@ -33,7 +33,7 @@ ...@@ -33,7 +33,7 @@
result; }) result; })
#endif #endif
#define CLOCK_REALTIME_RES TICK_NSEC(TICK_USEC) // In nano seconds. #define CLOCK_REALTIME_RES TICK_NSEC // In nano seconds.
static inline u64 mpy_l_X_l_ll(unsigned long mpy1,unsigned long mpy2) static inline u64 mpy_l_X_l_ll(unsigned long mpy1,unsigned long mpy2)
{ {
...@@ -1192,6 +1192,7 @@ do_clock_nanosleep(clockid_t which_clock, int flags, struct timespec *tsave) ...@@ -1192,6 +1192,7 @@ do_clock_nanosleep(clockid_t which_clock, int flags, struct timespec *tsave)
if (abs || !rq_time) { if (abs || !rq_time) {
adjust_abs_time(&posix_clocks[which_clock], &t, abs, adjust_abs_time(&posix_clocks[which_clock], &t, abs,
&rq_time); &rq_time);
rq_time += (t.tv_sec || t.tv_nsec);
} }
left = rq_time - get_jiffies_64(); left = rq_time - get_jiffies_64();
...@@ -1222,7 +1223,7 @@ do_clock_nanosleep(clockid_t which_clock, int flags, struct timespec *tsave) ...@@ -1222,7 +1223,7 @@ do_clock_nanosleep(clockid_t which_clock, int flags, struct timespec *tsave)
if (abs) if (abs)
return -ERESTARTNOHAND; return -ERESTARTNOHAND;
left *= TICK_NSEC(TICK_USEC); left *= TICK_NSEC;
tsave->tv_sec = div_long_long_rem(left, tsave->tv_sec = div_long_long_rem(left,
NSEC_PER_SEC, NSEC_PER_SEC,
&tsave->tv_nsec); &tsave->tv_nsec);
......
...@@ -337,7 +337,7 @@ int do_adjtimex(struct timex *txc) ...@@ -337,7 +337,7 @@ int do_adjtimex(struct timex *txc)
} /* txc->modes & ADJ_OFFSET */ } /* txc->modes & ADJ_OFFSET */
if (txc->modes & ADJ_TICK) { if (txc->modes & ADJ_TICK) {
tick_usec = txc->tick; tick_usec = txc->tick;
tick_nsec = TICK_NSEC(tick_usec); tick_nsec = TICK_USEC_TO_NSEC(tick_usec);
} }
} /* txc->modes */ } /* txc->modes */
leave: if ((time_status & (STA_UNSYNC|STA_CLOCKERR)) != 0 leave: if ((time_status & (STA_UNSYNC|STA_CLOCKERR)) != 0
......
...@@ -440,7 +440,7 @@ static inline void __run_timers(tvec_base_t *base) ...@@ -440,7 +440,7 @@ static inline void __run_timers(tvec_base_t *base)
* Timekeeping variables * Timekeeping variables
*/ */
unsigned long tick_usec = TICK_USEC; /* ACTHZ period (usec) */ unsigned long tick_usec = TICK_USEC; /* ACTHZ period (usec) */
unsigned long tick_nsec = TICK_NSEC(TICK_USEC); /* USER_HZ period (nsec) */ unsigned long tick_nsec = TICK_NSEC; /* USER_HZ period (nsec) */
/* /*
* The current time * The current time
...@@ -470,7 +470,7 @@ long time_precision = 1; /* clock precision (us) */ ...@@ -470,7 +470,7 @@ long time_precision = 1; /* clock precision (us) */
long time_maxerror = NTP_PHASE_LIMIT; /* maximum error (us) */ long time_maxerror = NTP_PHASE_LIMIT; /* maximum error (us) */
long time_esterror = NTP_PHASE_LIMIT; /* estimated error (us) */ long time_esterror = NTP_PHASE_LIMIT; /* estimated error (us) */
long time_phase; /* phase offset (scaled us) */ long time_phase; /* phase offset (scaled us) */
long time_freq = ((1000000 + HZ/2) % HZ - HZ/2) << SHIFT_USEC; long time_freq = (((NSEC_PER_SEC + HZ/2) % HZ - HZ/2) << SHIFT_USEC) / NSEC_PER_USEC;
/* frequency offset (scaled ppm)*/ /* frequency offset (scaled ppm)*/
long time_adj; /* tick adjust (scaled 1 / HZ) */ long time_adj; /* tick adjust (scaled 1 / HZ) */
long time_reftime; /* time at last adjustment (s) */ long time_reftime; /* time at last adjustment (s) */
...@@ -634,12 +634,12 @@ static void update_wall_time_one_tick(void) ...@@ -634,12 +634,12 @@ static void update_wall_time_one_tick(void)
* advance the tick more. * advance the tick more.
*/ */
time_phase += time_adj; time_phase += time_adj;
if (time_phase <= -FINEUSEC) { if (time_phase <= -FINENSEC) {
long ltemp = -time_phase >> (SHIFT_SCALE - 10); long ltemp = -time_phase >> (SHIFT_SCALE - 10);
time_phase += ltemp << (SHIFT_SCALE - 10); time_phase += ltemp << (SHIFT_SCALE - 10);
delta_nsec -= ltemp; delta_nsec -= ltemp;
} }
else if (time_phase >= FINEUSEC) { else if (time_phase >= FINENSEC) {
long ltemp = time_phase >> (SHIFT_SCALE - 10); long ltemp = time_phase >> (SHIFT_SCALE - 10);
time_phase -= ltemp << (SHIFT_SCALE - 10); time_phase -= ltemp << (SHIFT_SCALE - 10);
delta_nsec += ltemp; delta_nsec += ltemp;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment