If you have an SMP machine and have CPUs with different multipliers (i.e.
different speeds), Linux does not work correctly, at least not if it uses TSC
which is currently the case for Celerons (?) and better.
I am attaching a patch against 2.3.99-pre6 which attempts to fix this. It is a
PRELIMINARY patch which means I beg you to please send me some feedback. I
really am out of my league here with kernel hacking (so please be gentle).
However, the patch should detect the error condition and work around it, you
don't have to configure anything (but remember to do make dep).
Once the bugs are fixed and/or no bugs are found :), I'll send it along to the
appropriate people.
Please: I need this tested under the following environments:
ABIT BP6-style Celeron SMP motherboard with same speed CPUs
ABIT BP6-style Celeron SMP motherboard with different speed CPUs
PPro or better SMP machine with same speed CPUs
PPro or better SMP machine with different speed CPUs if such exists
Athlon (for the sake of consistency)
Cyrix CPU
And last but not least, a PPro-or-better and a worse-than-PPro SINGLE CPU
machine.
If you have any of these, please try out the patch and let me know how you fair.
The patch is my own work but with help from Alan Cox and this mailing list.
--
Christopher Thompson http://hypocrite.org/
One Ring to rule them all, One Ring to find them,
One Ring to bring them all and in the darkness bind them
-- Fellowship of the Rings, movie, Christmas 2001
diff -ur linux-2.3.99-pre6.clean/arch/i386/kernel/smpboot.c linux/arch/i386/kernel/smpboot.c
--- linux-2.3.99-pre6.clean/arch/i386/kernel/smpboot.c Wed Apr 12 10:33:20 2000
+++ linux/arch/i386/kernel/smpboot.c Wed May 10 18:40:39 2000
@@ -15,6 +15,7 @@
* later.
*
* Fixes
+ * Chris Thompson : Selectively disable TSC
* Felix Koop : NR_CPUS used properly
* Jose Renau : Handle single CPU case.
* Alan Cox : By repeated request 8) - Total BogoMIP report.
@@ -45,6 +46,8 @@
#include <asm/mtrr.h>
#include <asm/pgalloc.h>
+#include <linux/time.h>
+
/* Set if we find a B stepping CPU */
static int smp_b_stepping = 0;
@@ -969,14 +972,35 @@
printk(KERN_ERR "Error: only one processor found.\n");
} else {
unsigned long bogosum = 0;
+ unsigned long min_bogomips = 0, max_bogomips = 0;
+ unsigned long this_bogomips = 0;
for (cpu = 0; cpu < NR_CPUS; cpu++)
- if (cpu_online_map & (1<<cpu))
- bogosum += cpu_data[cpu].loops_per_sec;
+ if (cpu_online_map & (1<<cpu)) {
+ this_bogomips = cpu_data[cpu].loops_per_sec;
+ bogosum += this_bogomips;
+ if ( (min_bogomips == 0) ||
+ (min_bogomips > this_bogomips))
+ min_bogomips = this_bogomips;
+ if ( (max_bogomips == 0) ||
+ (max_bogomips < this_bogomips))
+ max_bogomips = this_bogomips;
+ }
printk(KERN_INFO "Total of %d processors activated (%lu.%02lu BogoMIPS).\n",
cpucount+1,
(bogosum+2500)/500000,
((bogosum+2500)/5000)%100);
Dprintk("Before bogocount - setting activated=1.\n");
+ /*
+ * Are CPUs within 5% of each other? Note: This
+ * may have to be altered once 5% is within one
+ * multiplier setting on SMP CPUs. However, you
+ * must allow a bit of slack.
+ */
+ if ( ((min_bogomips/100) * 105) > max_bogomips) {
+ printk(KERN_INFO "CPUs have the same speed, enabling TSC\n");
+ do_speedup_gettimeoffset();
+ } else printk(KERN_ERR "Error: CPUs have different speeds, disabling TSC\n");
+ /* TSC is disabled by default */
}
smp_num_cpus = cpucount + 1;
diff -ur linux-2.3.99-pre6.clean/arch/i386/kernel/time.c linux/arch/i386/kernel/time.c
--- linux-2.3.99-pre6.clean/arch/i386/kernel/time.c Wed Mar 15 18:00:03 2000
+++ linux/arch/i386/kernel/time.c Wed May 10 18:17:08 2000
@@ -113,9 +113,6 @@
}
#define TICK_SIZE tick
-
-#ifndef CONFIG_X86_TSC
-
spinlock_t i8253_lock = SPIN_LOCK_UNLOCKED;
/* This function must be called with interrupts disabled
@@ -232,13 +229,17 @@
return count;
}
+/*
+ * Default to using do_slow_gettimeoffset(). Later, we will
+ * upgrade to the do_fast_gettimeoffset() if our CPUs are
+ * running at the same multiplier.
+ */
static unsigned long (*do_gettimeoffset)(void) = do_slow_gettimeoffset;
-#else
-
-#define do_gettimeoffset() do_fast_gettimeoffset()
-
-#endif
+void do_speedup_gettimeoffset(void) {
+ printk(KERN_INFO "Selecting TSC gettimeoffset()\n");
+ do_gettimeoffset = do_fast_gettimeoffset;
+}
/*
* This version of gettimeofday has microsecond resolution
@@ -677,9 +678,6 @@
* 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.
diff -ur linux-2.3.99-pre6.clean/include/linux/time.h linux/include/linux/time.h
--- linux-2.3.99-pre6.clean/include/linux/time.h Wed Apr 26 16:29:06 2000
+++ linux/include/linux/time.h Wed May 10 18:15:05 2000
@@ -63,6 +63,7 @@
extern void do_settimeofday(struct timeval *tv);
extern void get_fast_time(struct timeval *tv);
extern void (*do_get_fast_time)(struct timeval *);
+extern void do_speedup_gettimeoffset(void);
#endif
#define FD_SETSIZE __FD_SETSIZE