linux-mips
[Top] [All Lists]

Re: kernel sources?

To: "Ralf Baechle" <ralf@oss.sgi.com>, "Geert Uytterhoeven" <geert@linux-m68k.org>
Subject: Re: kernel sources?
From: "Kevin D. Kissell" <kevink@mips.com>
Date: Sat, 15 Jan 2000 18:28:40 +0100
Cc: "Linux/MIPS" <linux@cthulhu.engr.sgi.com>
Sender: owner-linuxmips@oss.sgi.com
-----Original Message-----
From: Ralf Baechle <ralf@oss.sgi.com>
To: Geert Uytterhoeven <geert@linux-m68k.org>
Cc: Linux/MIPS <linux@cthulhu.engr.sgi.com>
Date: Saturday, January 15, 2000 1:56 AM
Subject: Re: kernel sources?


>On Thu, Jan 13, 2000 at 05:03:39PM +0100, Geert Uytterhoeven wrote:
>
>> I used the R5000 CP0_COUNTER/CP0_COMPARE registers for the timer interrupt. I
>> know it's not accurate, but it's better than nothing. I still have to figure
>> out how more complex interrupts work in the MIPS source tree.
>
>It is accurate as driven by the CPU's clock.  It is just somewhat tricky to
>handle and even more tricky on some broken CPUs.  From an old email written
>by Bill Earl:
>
>[...]
>As far as I know, all R4000 processors, and possibly some R4400 processors,
>are affected.  The bug is that, if you read $count exactly when it equals
>$compare, the count/compare interrupt for that count/compare crossing is
>discarded.  The workaround from IRIX is appended.  The variable
>r4000_clock_war is set to 1 if the system is an Indy with an R4000 processor.
>The r4k_compare_shadow is set to the same value as $compare whenever $compare
>is updated (with interrupts masked while the variable and $compare are
>updated together).
>[...]

There is also a race condition inherent in many implementations of
the timer interrupt handler, and the main stream of the current
MIPS/Linux distributions is no exception.   The SGI code looks
a bit like this:

void indy_timer_interrupt(struct pt_regs *regs)
{
        unsigned long count;
        int irq = 7;

        /* Ack timer and compute new compare. */
        count = read_32bit_cp0_register(CP0_COUNT);
        /* This has races.  */
        if ((count - r4k_cur) >= r4k_offset) {
                /* If this happens to often we'll need to compensate.  */
                missed_heart_beats++;
                r4k_cur = count + r4k_offset;
        }
        else
            r4k_cur += r4k_offset;
        ack_r4ktimer(r4k_cur);
        kstat.irqs[0][irq]++;
        do_timer(regs);

        /* We update the Dallas time of day approx. every 11 minutes,
         * because of how the numbers work out we need to make
         * absolutely sure we do this update within 500ms before the
         * next second starts, thus the following code.
         */
        if ((time_status & STA_UNSYNC) == 0 &&
            xtime.tv_sec > last_rtc_update + 660 &&
            xtime.tv_usec >= 500000 - (tick >> 1) &&
            xtime.tv_usec <= 500000 + (tick >> 1)) {
                if (set_rtc_mmss(xtime.tv_sec) == 0)
                        last_rtc_update = xtime.tv_sec;
                else
                        /* do it again in 60 s */
                        last_rtc_update = xtime.tv_sec - 600;
        }
}

The inherent race is in the fact that the count is sampled
once at the beginnning of the routine and used throughout.
It is possible (with bad luck and sloppy drivers) to be very
late into the handler, so much so that the new time-out time
can be reached between the sample and the programming
of the compare register.  If that happens, one gets no timer
interrupt for a full wrap of the count register.   I first observed
this in OpenBSD, where the problem was worse, but Linux
has the same conceptual hole.  A more robust handler
looks a bit like this:

void p5064_timer_interrupt(struct pt_regs *regs)
{
        int irq = 7;

        if(r4k_offset != 0) {
                do {
                        kstat.irqs[0][irq]++;
                        do_timer(regs);

                        /* Historical comment/code:
                        * RTC time of day s updated approx. every 11
                        * minutes.  Because of how the numbers work out
                        * we need to make absolutely sure we do this update
                        * within 500ms before the * next second starts,
                        * thus the following code.
                        */
                        if ((time_status & STA_UNSYNC) == 0
                        && xtime.tv_sec > last_rtc_update + 660
                        && xtime.tv_usec >= 500000 - (tick >> 1)
                        && xtime.tv_usec <= 500000 + (tick >> 1))
                            if (set_rtc_mmss(xtime.tv_sec) == 0)
                                last_rtc_update = xtime.tv_sec;
                            else
                                /* do it again in 60 s */
                                last_rtc_update = xtime.tv_sec - 600;

                        r4k_cur += r4k_offset;
                        ack_r4ktimer(r4k_cur);
                } while (((unsigned long)read_32bit_cp0_register(CP0_COUNT)
                        - r4k_cur) < 0x7fffffff);
        } else ack_r4ktimer(0);
}

As Ralf says, it's very accurate *if* you have an accurate picture of
the CPU's clock frequency.  The usual mechanism of measuring
the progress of the count register against elapsed time on a TOD
clock has been known to be inaccurate on some platforms due
to skew between the visibility to software and the actual timing
event, so be careful.

            Kevin K.
__

Kevin D. Kissell
MIPS Technologies European Architecture Lab
kevink@mips.com
Tel. +33.4.78.38.70.67
FAX. +33.4.78.38.70.68


<Prev in Thread] Current Thread [Next in Thread>