lkml.org 
[lkml]   [1997]   [Sep]   [16]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
From
Date
Subject2.0 (i386) patch: "completely mad time"
(Out of despair and lack of other good ideas I invented the "Complete Mad
Time (TM)":)

There's a difference with the timeoffset taken from the timer chip and
the fasttimeoffset taken from the Pentium cycle counter:

If a time interrupt is delayed with the Pentium stuff, the error
accumulates (e.g. when the first is 500µs late and the next is
200µs late, the total delay is 700µs). Due to the way the
fasttimeoffset is reset in the timer interrupt routine, these delays
are lost forever. I call this badness "1"

If a timer interrupt is delayed significantly (for more than one
tick), the current timeoffset is clamped at one tick. Thus the time
would no longer increase until the interrupt is processed. I call that
badness "2".

If you remove the clamping, the timeoffset can grow beyond one tick,
but on the next timer interrupt only one tick will be added and the
offset will be reset to zero, efectively makeing the time run
backwards. I call this badness "3".

I tried to fix these problems with my CMT (Completely Mad Time) patch
on top of 2.0.31-pre9 (The kernel should be tested anyway): The
clamping in the fast timeoffset has been removed, and the "cycles per
microsecond" is available more globally.

In the timer interrupt the delay is computed from the cycle counter
and added to the system clock. If the amount is larger than one tick
(very bad), only one tick is corrected and the remainder is
accumulated for later correction. If the delay is smaller, any
remaining correction from previous runs is subtracted from the system
time, but only up to one tick (because the time between the last to
invokations of the time routine was shorter than one tick then).

I haven't dealt with the APM case yet, and maybe some constants need
tuning (border cases). After all it looks not much worse than the
previous code. Is anyone out there that wants to test the patch? Ingo
Molnar still around?

There are two patches on top of 2.0.31-pre9 and against
arch/i386/kernel/time.c. Finally I have included a histogram of
measured time differences in a tight loop. Time doesn't look too
bad. The Program was taken from the xntp distribution (util/hist.c).

Have a nice time,
have a mad time,
have CMT

Ulrich

--- time.c 1997/09/12 19:35:53 1.2
+++ time.c 1997/09/15 19:19:36
@@ -52,7 +52,7 @@
{
register unsigned long eax asm("ax");
register unsigned long edx asm("dx");
- unsigned long tmp, quotient, low_timer, missing_time;
+ unsigned long tmp, quotient;

/* Last jiffy when do_fast_gettimeoffset() was called.. */
static unsigned long last_jiffies=0;
@@ -63,21 +63,11 @@
/* The "clocks per usec" value is calculated once each jiffy */
tmp = jiffies;
quotient = cached_quotient;
- low_timer = last_timer_cc.low;
- missing_time = 0;
if (last_jiffies != tmp) {
last_jiffies = tmp;
- /*
- * test for hanging bottom handler (this means xtime is not
- * updated yet)
- */
- if (test_bit(TIMER_BH, &bh_active) )
- {
- missing_time = 1000020/HZ;
- }

/* Get last timer tick in absolute kernel time */
- eax = low_timer;
+ eax = last_timer_cc.low;
edx = last_timer_cc.high;
__asm__("subl "SYMBOL_NAME_STR(init_timer_cc)",%0\n\t"
"sbbl "SYMBOL_NAME_STR(init_timer_cc)"+4,%1"
@@ -95,7 +85,7 @@
:"r" (tmp),
"0" (eax), "1" (edx));

- edx = 1000020/HZ;
+ edx = tick;
tmp = eax;
eax = 0;

@@ -112,28 +102,20 @@
:"=a" (eax), "=d" (edx));

/* .. relative to previous jiffy (32 bits is enough) */
- edx = 0;
- eax -= low_timer;
+ __asm__("subl "SYMBOL_NAME_STR(last_timer_cc)",%0\n\t"
+ "sbbl "SYMBOL_NAME_STR(last_timer_cc)"+4,%1"
+ :"=a" (eax), "=d" (edx)
+ :"0" (eax), "1" (edx));

/*
- * Time offset = (1000020/HZ * time_low) / quotient.
+ * Convert to microseconds
*/
-
__asm__("mull %2"
:"=a" (eax), "=d" (edx)
:"r" (quotient),
"0" (eax), "1" (edx));

- /*
- * Due to rounding errors (and jiffies inconsistencies),
- * we need to check the result so that we'll get a timer
- * that is monotonic.
- */
- if (edx >= 1000020/HZ)
- edx = 1000020/HZ-1;
-
- eax = edx + missing_time;
- return eax;
+ return edx;
}
#endif

--- arch/i386/kernel/time.c 1997/09/15 19:19:36 1.3
+++ arch/i386/kernel/time.c 1997/09/15 20:35:20
@@ -43,6 +43,9 @@
unsigned long high;
} init_timer_cc, last_timer_cc;

+/* Cached "clocks per usec" value.. */
+static unsigned long cached_quotient=0;
+
/*
* This is more assembly than C, but it's also rather
* timing-critical and we have to use assembler to get
@@ -57,12 +60,8 @@
/* Last jiffy when do_fast_gettimeoffset() was called.. */
static unsigned long last_jiffies=0;

- /* Cached "clocks per usec" value.. */
- static unsigned long cached_quotient=0;
-
/* The "clocks per usec" value is calculated once each jiffy */
tmp = jiffies;
- quotient = cached_quotient;
if (last_jiffies != tmp) {
last_jiffies = tmp;

@@ -94,8 +93,8 @@
:"r" (tmp),
"0" (eax), "1" (edx));
cached_quotient = eax;
- quotient = eax;
}
+ quotient = cached_quotient;

/* Read the time counter */
__asm__(".byte 0x0f,0x31"
@@ -360,6 +359,58 @@
}

#ifndef CONFIG_APM /* cycle counter may be unreliable */
+static void do_clock_correction(void)
+{
+ static long last_delay = 0;
+ register unsigned long eax asm("ax");
+ register unsigned long edx asm("dx");
+ unsigned long quotient = cached_quotient;
+ unsigned long usecs;
+
+ /* Read the time counter.. */
+ __asm__(".byte 0x0f,0x31"
+ :"=a" (eax), "=d" (edx));
+
+ /* ..relative to previous interrupt */
+ __asm__("subl "SYMBOL_NAME_STR(last_timer_cc)",%0\n\t"
+ "sbbl "SYMBOL_NAME_STR(last_timer_cc)"+4,%1"
+ :"=a" (eax), "=d" (edx)
+ :"0" (eax), "1" (edx));
+
+ /*
+ * Check if we are late
+ */
+ __asm__("mull %2"
+ :"=a" (eax), "=d" (edx)
+ :"r" (quotient),
+ "0" (eax), "1" (edx));
+ usecs = edx;
+ if (usecs > tick) {
+ /* add the amount that we are late */
+ if ((xtime.tv_usec += usecs - tick) > 1000000)
+ ++xtime.tv_sec, xtime.tv_usec -= 1000000;
+ last_delay += usecs - tick;
+ }
+ else if ( last_delay > 0) {
+ /* we are on schedule, but were late previously */
+ /* as we should reduce tick now, we just reduce the time to
+ make it correct again */
+ if ( last_delay > tick ) {
+ /* we have a slight problem here */
+ xtime.tv_usec -= tick;
+ last_delay -= tick;
+ }
+ else {
+ xtime.tv_usec -= last_delay;
+ last_delay = 0;
+ }
+ if (xtime.tv_usec < 0)
+ --xtime.tv_sec, xtime.tv_usec += 1000000;
+ /* NOTE: Time won't go backwards (we'll add another tick) */
+
+ }
+}
+
/*
* 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
@@ -367,10 +418,14 @@
*/
static void pentium_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
- /* read Pentium cycle counter */
+ do_clock_correction();
+ /* read Pentium cycle counter and let's forget that we were late
+ * (that's stored in last_delay
+ */
__asm__(".byte 0x0f,0x31"
:"=a" (last_timer_cc.low),
"=d" (last_timer_cc.high));
+
timer_interrupt(irq, NULL, regs);
}
#endif
# Histogram: time between calls, number of occurrencies
4 11690501
5 110328542
6 36456
7 330
8 18
9 1
15 3188
16 29589
17 21253
18 4513
19 883
20 379
21 306
22 276
23 302
24 279
25 119
26 48
27 73
28 650
29 563
30 143
31 24
32 3
...
396 1
401 1
427 1
438 1
439 1
443 1
456 1
496 1
524 1
525 1
606 1
755 1
962 2
1301 1
1428 1
1669 1
1836 1
1865 1
1870 1
1889 1
2400 1
6301 1
6309 1
12876 1

\
 
 \ /
  Last update: 2005-03-22 13:40    [W:0.144 / U:0.040 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site