From: george anzinger <george@mvista.com>
To: Andrew Morton <akpm@digeo.com>
Cc: "linux-kernel@vger.kernel.org" <linux-kernel@vger.kernel.org>,
Eric Piel <Eric.Piel@Bull.Net>
Subject: [PATCH] More time clean up stuff.
Date: Mon, 09 Jun 2003 16:54:16 -0700 [thread overview]
Message-ID: <3EE51E28.40903@mvista.com> (raw)
[-- Attachment #1: Type: text/plain, Size: 1212 bytes --]
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.
(Note, this patch depends on the prior timeclean up patch of this date.)
--
George Anzinger george@mvista.com
High-res-timers: http://sourceforge.net/projects/high-res-timers/
Preemption patch: http://www.kernel.org/pub/linux/kernel/people/rml
[-- Attachment #2: timeclean-eric-2.5.70-1.0.patch --]
[-- Type: text/plain, Size: 11310 bytes --]
diff -urP -I '\$Id:.*Exp \$' -X /usr/src/patch.exclude linux-2.5.70-time_save/arch/i386/kernel/time.c linux/arch/i386/kernel/time.c
--- linux-2.5.70-time_save/arch/i386/kernel/time.c 2003-05-30 01:29:48.000000000 -0700
+++ linux/arch/i386/kernel/time.c 2003-06-09 16:23:04.000000000 -0700
@@ -127,7 +127,7 @@
* made, and then undo it!
*/
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) {
tv->tv_nsec += NSEC_PER_SEC;
diff -urP -I '\$Id:.*Exp \$' -X /usr/src/patch.exclude linux-2.5.70-time_save/include/asm-i386/mach-pc9800/setup_arch_pre.h linux/include/asm-i386/mach-pc9800/setup_arch_pre.h
--- linux-2.5.70-time_save/include/asm-i386/mach-pc9800/setup_arch_pre.h 2003-03-24 23:34:12.000000000 -0800
+++ linux/include/asm-i386/mach-pc9800/setup_arch_pre.h 2003-06-09 16:03:59.000000000 -0700
@@ -26,7 +26,7 @@
CLOCK_TICK_RATE = PC9800_8MHz_P() ? 1996800 : 2457600;
printk(KERN_DEBUG "CLOCK_TICK_RATE = %d\n", CLOCK_TICK_RATE);
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;
#ifdef CONFIG_SMP
diff -urP -I '\$Id:.*Exp \$' -X /usr/src/patch.exclude linux-2.5.70-time_save/include/asm-ia64/timex.h linux/include/asm-ia64/timex.h
--- linux-2.5.70-time_save/include/asm-ia64/timex.h 2002-09-26 11:24:18.000000000 -0700
+++ linux/include/asm-ia64/timex.h 2003-06-09 16:03:59.000000000 -0700
@@ -14,7 +14,11 @@
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
get_cycles (void)
diff -urP -I '\$Id:.*Exp \$' -X /usr/src/patch.exclude linux-2.5.70-time_save/include/linux/time.h linux/include/linux/time.h
--- linux-2.5.70-time_save/include/linux/time.h 2003-05-30 01:29:48.000000000 -0700
+++ linux/include/linux/time.h 2003-06-09 16:03:59.000000000 -0700
@@ -69,10 +69,11 @@
#ifndef NSEC_PER_USEC
#define NSEC_PER_USEC (1000L)
#endif
+
/*
* 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
- * 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
* with a scales defined here as SEC_JIFFIE_SC, USEC_JIFFIE_SC and
* NSEC_JIFFIE_SC. Note that these defines contain nothing but
@@ -83,23 +84,30 @@
#define NSEC_JIFFIE_SC (SEC_JIFFIE_SC + 30)
#define USEC_JIFFIE_SC (SEC_JIFFIE_SC + 20)
#define SEC_CONVERSION ((unsigned long)(((u64)NSEC_PER_SEC << SEC_JIFFIE_SC) /\
- (u64)TICK_NSEC(TICK_USEC)))
-#define NSEC_CONVERSION ((unsigned long)(((u64)1 << NSEC_JIFFIE_SC) / \
- (u64)TICK_NSEC(TICK_USEC)))
-#define USEC_CONVERSION \
- ((unsigned long)(((u64)NSEC_PER_USEC << USEC_JIFFIE_SC)/ \
- (u64)TICK_NSEC(TICK_USEC)))
-#define MAX_SEC_IN_JIFFIES \
- (u32)((u64)((u64)MAX_JIFFY_OFFSET * TICK_NSEC(TICK_USEC)) / NSEC_PER_SEC)
+ (u64)TICK_NSEC))
+#define NSEC_CONVERSION ((unsigned long)(((u64)1 << NSEC_JIFFIE_SC) /\
+ (u64)TICK_NSEC))
+#define USEC_CONVERSION ((unsigned long)(((u64)NSEC_PER_USEC << USEC_JIFFIE_SC)/\
+ (u64)TICK_NSEC))
+#if BITS_PER_LONG < 64
+# define MAX_SEC_IN_JIFFIES \
+ (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
timespec_to_jiffies(struct timespec *value)
{
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)
- return MAX_JIFFY_OFFSET;
+ if (sec >= MAX_SEC_IN_JIFFIES){
+ sec = MAX_SEC_IN_JIFFIES;
+ nsec = 0;
+ }
return (((u64)sec * SEC_CONVERSION) +
(((u64)nsec * NSEC_CONVERSION) >>
(NSEC_JIFFIE_SC - SEC_JIFFIE_SC))) >> SEC_JIFFIE_SC;
@@ -113,7 +121,7 @@
* Convert jiffies to nanoseconds and seperate with
* 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);
}
@@ -122,10 +130,12 @@
timeval_to_jiffies(struct timeval *value)
{
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)
- return MAX_JIFFY_OFFSET;
+ if (sec >= MAX_SEC_IN_JIFFIES){
+ sec = MAX_SEC_IN_JIFFIES;
+ usec = 0;
+ }
return (((u64)sec * SEC_CONVERSION) +
(((u64)usec * USEC_CONVERSION) >>
(USEC_JIFFIE_SC - SEC_JIFFIE_SC))) >> SEC_JIFFIE_SC;
@@ -138,7 +148,7 @@
* Convert jiffies to nanoseconds and seperate with
* 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_usec /= NSEC_PER_USEC;
}
diff -urP -I '\$Id:.*Exp \$' -X /usr/src/patch.exclude linux-2.5.70-time_save/include/linux/timex.h linux/include/linux/timex.h
--- linux-2.5.70-time_save/include/linux/timex.h 2003-05-05 15:34:09.000000000 -0700
+++ linux/include/linux/timex.h 2003-06-09 16:03:59.000000000 -0700
@@ -99,19 +99,19 @@
* variable which serves as an extension to the low-order bits of the
* system clock variable. The SHIFT_UPDATE define establishes the decimal
* 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.
*
* 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.
+ * FINENSEC is 1 ns in SHIFT_UPDATE units of the time_phase variable.
*/
#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 FINENSEC (1L << (SHIFT_SCALE - 10)) /* ~1 ns in phase units */
#define MAXPHASE 512000L /* max phase error (us) */
#define MAXFREQ (512L << SHIFT_USEC) /* max frequency error (ppm) */
@@ -159,7 +159,7 @@
* (NOM << LSH) / DEN
* 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
- * 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.
*/
@@ -169,12 +169,16 @@
/* HZ is the requested value. ACTHZ is actual HZ ("<< 8" is for accuracy) */
#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 */
-#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) */
-#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>
/*
diff -urP -I '\$Id:.*Exp \$' -X /usr/src/patch.exclude linux-2.5.70-time_save/kernel/posix-timers.c linux/kernel/posix-timers.c
--- linux-2.5.70-time_save/kernel/posix-timers.c 2003-06-09 13:26:30.000000000 -0700
+++ linux/kernel/posix-timers.c 2003-06-09 16:15:14.000000000 -0700
@@ -33,7 +33,7 @@
result; })
#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)
{
@@ -1201,7 +1201,7 @@
if (abs || !rq_time) {
adjust_abs_time(&posix_clocks[which_clock], &t, abs,
&rq_time);
- // rq_time += (t.tv_sec || t.tv_nsec);
+ rq_time += (t.tv_sec || t.tv_nsec);
}
left = rq_time - get_jiffies_64();
@@ -1232,7 +1232,7 @@
if (abs)
return -ERESTARTNOHAND;
- left *= TICK_NSEC(TICK_USEC);
+ left *= TICK_NSEC;
tsave->tv_sec = div_long_long_rem(left,
NSEC_PER_SEC,
&tsave->tv_nsec);
diff -urP -I '\$Id:.*Exp \$' -X /usr/src/patch.exclude linux-2.5.70-time_save/kernel/time.c linux/kernel/time.c
--- linux-2.5.70-time_save/kernel/time.c 2003-05-30 01:29:48.000000000 -0700
+++ linux/kernel/time.c 2003-06-09 16:03:59.000000000 -0700
@@ -339,7 +339,7 @@
} /* txc->modes & ADJ_OFFSET */
if (txc->modes & ADJ_TICK) {
tick_usec = txc->tick;
- tick_nsec = TICK_NSEC(tick_usec);
+ tick_nsec = TICK_USEC_TO_NSEC(tick_usec);
}
} /* txc->modes */
leave: if ((time_status & (STA_UNSYNC|STA_CLOCKERR)) != 0
diff -urP -I '\$Id:.*Exp \$' -X /usr/src/patch.exclude linux-2.5.70-time_save/kernel/timer.c linux/kernel/timer.c
--- linux-2.5.70-time_save/kernel/timer.c 2003-05-30 01:29:48.000000000 -0700
+++ linux/kernel/timer.c 2003-06-09 16:19:46.000000000 -0700
@@ -439,7 +439,7 @@
* Timekeeping variables
*/
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
@@ -469,7 +469,7 @@
long time_maxerror = NTP_PHASE_LIMIT; /* maximum error (us) */
long time_esterror = NTP_PHASE_LIMIT; /* estimated error (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)*/
long time_adj; /* tick adjust (scaled 1 / HZ) */
long time_reftime; /* time at last adjustment (s) */
@@ -631,12 +631,12 @@
* advance the tick more.
*/
time_phase += time_adj;
- if (time_phase <= -FINEUSEC) {
+ if (time_phase <= -FINENSEC) {
long ltemp = -time_phase >> (SHIFT_SCALE - 10);
time_phase += ltemp << (SHIFT_SCALE - 10);
xtime.tv_nsec -= ltemp;
}
- else if (time_phase >= FINEUSEC) {
+ else if (time_phase >= FINENSEC) {
long ltemp = time_phase >> (SHIFT_SCALE - 10);
time_phase -= ltemp << (SHIFT_SCALE - 10);
xtime.tv_nsec += ltemp;
next reply other threads:[~2003-06-09 23:42 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2003-06-09 23:54 george anzinger [this message]
-- strict thread matches above, loose matches on Subject: below --
2003-06-10 0:57 [PATCH] More time clean up stuff george anzinger
2003-06-10 8:18 ` Riley Williams
2003-06-10 11:52 ` Eric Piel
2003-06-10 15:40 ` george anzinger
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=3EE51E28.40903@mvista.com \
--to=george@mvista.com \
--cc=Eric.Piel@Bull.Net \
--cc=akpm@digeo.com \
--cc=linux-kernel@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.