Re: Timestamp counters on SMP

From: Pavel Machek (pavel@suse.cz)
Date: Sat Apr 01 2000 - 13:28:23 EST


Hi!

> > Anyway, this proves that there indeed are such notebooks that don't
> > change just the duty cycle, but also the clock. And, it also means that
> > the kernel should take this into account. While I think it's not very
> > likely we can compensate for it (remeasure the TSC speed after every
> > APM/ACPI call?), we should at least be able to detect it somehow and
> > disable the use of TSC in the case its speed varies.
> >
> > Or at least issue a warning, like Pavel's kernel does, so that the user
> > notices the problem and disables TSC in the kernel config. I suppose
> > most users live with this unnoticed and causing problems.
> >
>
> It's probably impossible to detect; we probably should provide a no-tsc
> kernel flag though.

It is very easy to detect few ticks after that happens:

                                                                Pavel

--- clean/arch/i386/kernel/time.c Thu Mar 23 22:33:26 2000
+++ linux/arch/i386/kernel/time.c Mon Mar 20 10:15:08 2000
@@ -150,7 +150,7 @@
  * comp.protocols.time.ntp!
  */
 
-static unsigned long do_slow_gettimeoffset(void)
+static unsigned long do_read_hwtimer(void)
 {
         int count;
 
@@ -216,7 +216,7 @@
 
                                 count -= 256;
 #else
- printk("do_slow_gettimeoffset(): hardware timer problem?\n");
+ printk("do_read_hwtimer(): hardware timer problem?\n");
 #endif
                         }
                 }
@@ -225,6 +225,12 @@
         spin_unlock(&i8253_lock);
 
         count_p = count;
+ return count;
+}
+
+unsigned long do_slow_gettimeoffset(void)
+{
+ unsigned long count = do_read_hwtimer();
 
         count = ((LATCH-1) - count) * TICK_SIZE;
         count = (count + LATCH/2) / LATCH;
@@ -441,6 +447,27 @@
          */
         write_lock(&xtime_lock);
 
+ if (use_tsc) {
+ long off = do_gettimeoffset();
+ static int faster = 0, slower = 0;
+ if (off > (1100000/HZ))
+ faster++;
+ else
+ faster = 0;
+
+ if (off < (900000/HZ))
+ slower++;
+ else
+ slower = 0;
+
+ if ((faster > 100) || (slower > 100)) {
+ printk( KERN_ERR "TSC is %s than it should be! ", (faster > 10) ? "faster" : "slower" );
+ big_calibrate_tsc();
+ faster = 0;
+ slower = 0;
+ }
+ }
+
         if (use_tsc)
         {
                 /*
@@ -568,7 +595,7 @@
 #define CALIBRATE_LATCH (5 * LATCH)
 #define CALIBRATE_TIME (5 * 1000020/HZ)
 
-static unsigned long __init calibrate_tsc(void)
+static unsigned long calibrate_tsc(void)
 {
        /* Set the Gate high, disable speaker */
         outb((inb(0x61) & ~0x02) | 0x01, 0x61);
@@ -633,6 +660,38 @@
         return 0;
 }
 
+void big_calibrate_tsc(void)
+{
+ if (boot_cpu_data.x86_capability & X86_FEATURE_TSC) {
+ unsigned long tsc_quotient = calibrate_tsc();
+ if (tsc_quotient) {
+ fast_gettimeoffset_quotient = tsc_quotient;
+ use_tsc = 1;
+ /*
+ * We could be more selective here I suspect
+ * and just enable this for the next intel chips ?
+ */
+// x86_udelay_tsc = 1;
+#ifndef do_gettimeoffset
+ do_gettimeoffset = do_fast_gettimeoffset;
+#endif
+ do_get_fast_time = do_gettimeofday;
+
+ /* report CPU clock rate in Hz.
+ * The formula is (10^6 * 2^32) / (2^32 * 1 / (clocks/us)) =
+ * clock/second. Our precision is about 100 ppm.
+ */
+ { unsigned long eax=0, edx=1000000;
+ __asm__("divl %2"
+ :"=a" (cpu_hz), "=d" (edx)
+ :"r" (tsc_quotient),
+ "0" (eax), "1" (edx));
+ printk("Detected %ld Hz processor.\n", cpu_hz);
+ }
+ }
+ }
+}
+
 void __init time_init(void)
 {
         extern int x86_udelay_tsc;
@@ -640,6 +699,7 @@
         xtime.tv_sec = get_cmos_time();
         xtime.tv_usec = 0;
 
+
 /*
  * If we have APM enabled or the CPU clock speed is variable
  * (CPU stops clock on HLT or slows clock to save power)
@@ -667,34 +727,7 @@
  
          dodgy_tsc();
          
- if (boot_cpu_data.x86_capability & X86_FEATURE_TSC) {
- unsigned long tsc_quotient = calibrate_tsc();
- if (tsc_quotient) {
- fast_gettimeoffset_quotient = tsc_quotient;
- use_tsc = 1;
- /*
- * We could be more selective here I suspect
- * and just enable this for the next intel chips ?
- */
- x86_udelay_tsc = 1;
-#ifndef do_gettimeoffset
- do_gettimeoffset = do_fast_gettimeoffset;
-#endif
- do_get_fast_time = do_gettimeofday;
-
- /* report CPU clock rate in Hz.
- * The formula is (10^6 * 2^32) / (2^32 * 1 / (clocks/us)) =
- * clock/second. Our precision is about 100 ppm.
- */
- { unsigned long eax=0, edx=1000000;
- __asm__("divl %2"
- :"=a" (cpu_hz), "=d" (edx)
- :"r" (tsc_quotient),
- "0" (eax), "1" (edx));
- printk("Detected %ld Hz processor.\n", cpu_hz);
- }
- }
- }
+ big_calibrate_tsc();
 
 #ifdef CONFIG_VISWS
         printk("Starting Cobalt Timer system clock\n");

> The no-tsc flag should clear the tsc bit in the capabilities, and also
> set the bit in CR4 that disables access to it from user space.

I agree we need no-tsc flag.
                                                                Pavel

-- 
I'm pavel@ucw.cz. "In my country we have almost anarchy and I don't care."
Panos Katsaloulis describing me w.r.t. patents me at discuss@linmodems.org

- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.rutgers.edu Please read the FAQ at http://www.tux.org/lkml/



This archive was generated by hypermail 2b29 : Fri Apr 07 2000 - 21:00:08 EST