A patch for linux 2.1.127

H.J. Lu (hjl@lucon.org)
Sun, 8 Nov 1998 11:28:29 -0800 (PST)


Hi,

Here is a patch for Linux 2.1.127. Now I am running Linux 2.1.127
compiled with egcs 1.1.1 prerelease using -O6.

I don't know whom to blame. I have to say both. As you can see,
calibrate_tsc is just asm code written in the asm statement. It
does't make much sense to let compiler choose registers in that
case since most of registers are used in the asm statement and
there are only a few free registers left. Why not we just finish
the register allocation ourselves. The C compiler cannot do any
much better than it.

The problem starts when egcs 1.1.1 inlines calibrate_tsc. For

: "r" (5 * 1000020/HZ)

It puts 5 * 1000020/HZ in eax. It is overwritten by

"inb $0x61, %%al\n\t" /* Read port */

If egcs does a better job, it should know eax will be clobbered in
the asm statement and choose an unclobbered regiister instead. The
interesting thing is it only happens when calibrate_tsc is inlined.
Maybe the constraints in the asm statement are not 100% correct.

Anyway, I am enclosing the relevant part here. I hope someone can
give a better answer than mine.

Thanks.

-- 
H.J. Lu (hjl@gnu.org)
----
Index: arch/i386/kernel/time.c
===================================================================
RCS file: /home/work/cvs/linux/linux/arch/i386/kernel/time.c,v
retrieving revision 1.1.1.14
diff -u -p -r1.1.1.14 time.c
--- time.c	1998/10/24 13:53:40	1.1.1.14
+++ time.c	1998/11/08 19:14:04
@@ -566,8 +566,8 @@ __initfunc(static unsigned long calibrat
                "divl %%ecx\n\t" /* eax= 2^32 / (1 * TSC counts per microsecond) */
 	       /* Return eax for the use of fast_gettimeoffset */
                "movl %%eax, %0\n\t"
-               : "=r" (retval)
-               : "r" (5 * 1000020/HZ)
+               : "=S" (retval)
+               : "D" (5 * 1000020/HZ)
                : /* we clobber: */ "ax", "bx", "cx", "dx", "cc", "memory");
        return retval;
 }
----
__initfunc(static unsigned long calibrate_tsc(void))
{
       unsigned long retval;

__asm__( /* set the Gate high, program CTC channel 2 for mode 0 * (interrupt on terminal count mode), binary count, * load 5 * LATCH count, (LSB and MSB) * to begin countdown, read the TSC and busy wait. * BTW LATCH is calculated in timex.h from the HZ value */

/* Set the Gate high, disable speaker */ "inb $0x61, %%al\n\t" /* Read port */ "andb $0xfd, %%al\n\t" /* Turn off speaker Data */ "orb $0x01, %%al\n\t" /* Set Gate high */ "outb %%al, $0x61\n\t" /* Write port */

/* Now let's take care of CTC channel 2 */ "movb $0xb0, %%al\n\t" /* binary, mode 0, LSB/MSB, ch 2*/ "outb %%al, $0x43\n\t" /* Write to CTC command port */ "movb $0x0c, %%al\n\t" "outb %%al, $0x42\n\t" /* LSB of count */ "movb $0xe9, %%al\n\t" "outb %%al, $0x42\n\t" /* MSB of count */

/* Read the TSC; counting has just started */ "rdtsc\n\t" /* Move the value for safe-keeping. */ "movl %%eax, %%ebx\n\t" "movl %%edx, %%ecx\n\t"

/* Busy wait. Only 50 ms wasted at boot time. */ "0: inb $0x61, %%al\n\t" /* Read Speaker Output Port */ "testb $0x20, %%al\n\t" /* Check CTC channel 2 output (bit 5) */ "jz 0b\n\t"

/* And read the TSC. 5 jiffies (50.00077ms) have elapsed. */ "rdtsc\n\t"

/* Great. So far so good. Store last TSC reading in * last_tsc_low (only 32 lsb bits needed) */ "movl %%eax, last_tsc_low\n\t" /* And now calculate the difference between the readings. */ "subl %%ebx, %%eax\n\t" "sbbl %%ecx, %%edx\n\t" /* 64-bit subtract */ /* but probably edx = 0 at this point (see below). */ /* Now we have 5 * (TSC counts per jiffy) in eax. We want * to calculate TSC->microsecond conversion factor. */

/* Note that edx (high 32-bits of difference) will now be * zero iff CPU clock speed is less than 85 GHz. Moore's * law says that this is likely to be true for the next * 12 years or so. You will have to change this code to * do a real 64-by-64 divide before that time's up. */ "movl %%eax, %%ecx\n\t" "xorl %%eax, %%eax\n\t" "movl %1, %%edx\n\t" "divl %%ecx\n\t" /* eax= 2^32 / (1 * TSC counts per microsecond) */ /* Return eax for the use of fast_gettimeoffset */ "movl %%eax, %0\n\t" : "=S" (retval) : "D" (5 * 1000020/HZ) : /* we clobber: */ "ax", "bx", "cx", "dx", "cc", "memory"); return retval; }

__initfunc(void time_init(void)) { 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) * then the TSC timestamps may diverge by up to 1 jiffy from * 'real time' but nothing will break. * The most frequent case is that the CPU is "woken" from a halt * state by the timer interrupt itself, so we get 0 error. In the * rare cases where a driver would "wake" the CPU and request a * timestamp, the maximum error is < 1 jiffy. But timestamps are * still perfectly ordered. * Note that the TSC counter will be reset if APM suspends * to disk; this won't break the kernel, though, 'cuz we're * smart. See devices/char/apm_bios.c. */ if (boot_cpu_data.x86_capability & X86_FEATURE_TSC) { do_gettimeoffset = do_fast_gettimeoffset; do_get_fast_time = do_gettimeofday; irq0.handler = pentium_timer_interrupt; fast_gettimeoffset_quotient = calibrate_tsc(); /* 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" (fast_gettimeoffset_quotient), "0" (eax), "1" (edx)); printk("Detected %ld Hz processor.\n", cpu_hz); } } setup_x86_irq(0, &irq0); }

- 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/