If you've tried to use do_gettimeofday from within an RTAI-patched
kernel (and maybe RTLinux, but I can't confirm this since I don't use it,
but I've been told that this applies to RTLinux too), then you've noticed
that the times reported aren't accurate. There is "back to the future"
effect ... so to speak.

The real problem is with the fact that Linux depends on timer 0 of
the PIT (8254) to keep track of time. It expects to have it's
timer-interrupt handling routines be called once every 10ms. All
time keeping in Linux is done with this in mind. If this isn't
true, and it certainly isn't once RTAI starts playing around with
timer 0, then do_gettimeofday reports wrong times.

It is important to stress, that this isn't a locking problem.
Locks can come and go and xtime can be played around in Linux as
Linux feels free to do so, the real issue is in the timer_interrupt
function in arch/i386/kernel/time.c and the way it increments Linux's
internal variables to keep track of the 10ms that just passed.

In the fix I've done, rather than reading the latched value of
timer 0, the TSC is used to update the time (yes, this is only for
Pentium machines, I don't have any other type of machine and don't
have the time to do the tests even if I had one). We assume (correctly)
that RTAI doesn't write into the TSC. Therefore, whatever value is
in there reflects the machine's behavior.

The TSC's value is read and a calculation is made on the time
that has passed since the last time timer_interrupt() was called.
Keep in mind that since RTAI might be runing, this might be over
10ms. The lost_ticks variable is incremented for every 10ms lost
and decremented once since do_timer, which is called as part of
timer-interrupt handling, will increment this once. Then, and this
is the most important part, last_tsc_low is modified to reflect
the state of the machine as it should have been had the timer
interrupt really occured at 10ms interval. Therefore, the next
time do_gettimeofday is called and it ends up calling of
do_gettimeoffset, the difference between the current TSC's
value and the "supposed" last interrupt will yield an accurate
result.

This is the fixed timer_interrupt():
static void timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
        int count;
        unsigned long tsc_value, tsc_delta, micros_elapsed;
        extern volatile unsigned long lost_ticks;

        /*
         * Here we are in the timer irq handler. We just have irqs locally
         * disabled but we don't know if the timer_bh is running on the other
         * CPU. We need to avoid to SMP race with it. NOTE: we don' t need
         * the irq version of write_lock because as just said we have irq
         * locally disabled. -arca
         */
        write_lock(&xtime_lock);


        /* This is for RTAI only */
        if (use_tsc)
        {
                /* Read the TSC's value */
                rdtscl(tsc_value);

                /* Compute difference between the current TSC value and the last low 
val */
                tsc_delta = tsc_value - last_tsc_low;

                /* Time elapsed since last interrupt */
                {
                unsigned long eax=tsc_delta, edx=0;
                __asm__("mull %2"
                        :"=a" (eax), "=d" (micros_elapsed)
                        :"g" (fast_gettimeoffset_quotient),
                        "0" (eax), "1" (edx));
                }

                /* Did 10ms pass since the last time the TSC's low value was updated */
                if(micros_elapsed >= 10000)
                {
                        /* Loop until we've taken in account all 10ms lost interrupts 
*/
                        do
                        {
                                /* We've lost one tick */
                                lost_ticks++;

                                /* We've taken in account 10ms */
                                micros_elapsed -= 10000;
                        } while (micros_elapsed >= 10000);

                        /* We've incremented one lost-tick too many since do_timer 
will increment one more */
                        lost_ticks--;
                }

                /* Update the TSC low val (this should be good t'il we reach 4 GHz 
K.Y.) */
                /* 2 divisions are done to keep precision as best can be */
                last_tsc_low = tsc_value - (micros_elapsed * (cpu_hz / 10000)) / 100;
        }


#if 0
        if (use_tsc)
        {
          
                /*
                 * It is important that these two operations happen almost at
                 * the same time. We do the RDTSC stuff first, since it's
                 * faster. To avoid any inconsistencies, we need interrupts
                 * disabled locally.
                 */

                /*
                 * Interrupts are just disabled locally since the timer irq
                 * has the SA_INTERRUPT flag set. -arca
                 */
        
                /* read Pentium cycle counter */

                rdtscl(last_tsc_low);

                outb_p(0x00, 0x43);     /* latch the count ASAP */

                count = inb_p(0x40);    /* read the latched count */
                count |= inb(0x40) << 8;

                count = ((LATCH-1) - count) * TICK_SIZE;
//              delay_at_last_interrupt = (count + LATCH/2) / LATCH;
        }
#endif
 
        do_timer_interrupt(irq, NULL, regs);

        write_unlock(&xtime_lock);
}

This is it. You don't need to do anything else to fix the
results of do_gettimeofday.

If you want to use do_gettimeofday, though, from within an RTAI tasks,
there are other things you have to do. You actually have 2 options,
but before covering these, let us discuss why you can't use
do_gettimeofday from within an RTAI task. Well, the reason is simple,
do_gettimeofday uses normal Linux locks on xtime_lock to lock the
xtime variable and these locks are caught by RTAI. Therefore, your
machine will probably reboot or hang or crash or a combination of those
if you call on do_gettimeofday from within an RTAI task.

The 2 ways of solving this are:
1) Modify all locking on xtime to use real-time locks (rt_spinlock_irqsave
or something of the sort). This, though, has the disavantage of granting
any part of Linux "supperiority" on RTAI.
2) Add a new do_gettimeofday that is placed in the same file
(arch/i386/kernel/time.c), has the same prototypes and is almost
identical, except that it doesn't do any locking at all. This could
be called do_gettimeofday_nolock(). This should not be a problem
since any RTAI process runing has priority on Linux and therefore it
is sure that there will be no ressource contention over xtime. Moreover,
nothing in RTAI modifies xtime (which by this time you should have
understood is used by do_gettimeofday to compute the time).

The code I've written to fix do_gettimeofday isn't the most efficient
that can be written to do the job, but it works. Feel free to optimize
this and use it as you wish. If this can be used by RTLinux folks then
so be it.

Best regards.

Karim

===================================================
                 Karim Yaghmour
               [EMAIL PROTECTED]
          Operating System Consultant
 (Linux kernel, real-time and distributed systems)
===================================================
-- [rtl] ---
To unsubscribe:
echo "unsubscribe rtl" | mail [EMAIL PROTECTED] OR
echo "unsubscribe rtl <Your_email>" | mail [EMAIL PROTECTED]
---
For more information on Real-Time Linux see:
http://www.rtlinux.org/rtlinux/

Reply via email to