This patch fixes some SMP related time issue: . We can't get accurate do_gettimeoffset on swarm. This patch at least returns monotonically increasing time. . Lock protection scope is not right. . It is not right for low level interrupt handler to dispatch IRQ two on CPU 0 (one for system timer, and one for local timer) Instead, we now let sytem timer routine invoke local timer tasks. Jun diff -Nru linux/arch/mips/kernel/time.c.orig linux/arch/mips/kernel/time.c --- linux/arch/mips/kernel/time.c.orig Wed Apr 30 09:47:22 2003 +++ linux/arch/mips/kernel/time.c Wed May 28 16:40:49 2003 @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -79,7 +80,7 @@ * is nonzero if the timer bottom half hasnt executed yet. */ if (jiffies - wall_jiffies) - tv->tv_usec += USECS_PER_JIFFY; + tv->tv_usec += USECS_PER_JIFFY * (jiffies - wall_jiffies); read_unlock_irqrestore (&xtime_lock, flags); @@ -344,7 +345,7 @@ count = read_c0_count(); /* check to see if we have missed any timer interrupts */ - if ((count - expirelo) < 0x7fffffff) { + if (time_after(count, expirelo)) { /* missed_timer_count ++; */ expirelo = count + cycles_per_jiffy; write_c0_compare(expirelo); @@ -355,6 +356,8 @@ timerlo = count; } + write_lock (&xtime_lock); + /* * call the generic timer interrupt handling */ @@ -365,7 +368,6 @@ * CMOS clock accordingly every ~11 minutes. rtc_set_time() has to be * called as close as possible to 500 ms before the new second starts. */ - read_lock (&xtime_lock); if ((time_status & STA_UNSYNC) == 0 && xtime.tv_sec > last_rtc_update + 660 && xtime.tv_usec >= 500000 - ((unsigned) tick) / 2 && @@ -377,7 +379,6 @@ /* do it again in 60 s */ } } - read_unlock (&xtime_lock); /* * If jiffies has overflowed in this timer_interrupt we must @@ -388,26 +389,20 @@ timerhi = timerlo = 0; } -#if !defined(CONFIG_SMP) + write_unlock (&xtime_lock); + /* - * In UP mode, we call local_timer_interrupt() to do profiling - * and process accouting. - * - * In SMP mode, local_timer_interrupt() is invoked by appropriate - * low-level local timer interrupt handler. + * We call local_timer_interrupt() to do profiling and + * process accouting. */ local_timer_interrupt(0, NULL, regs); -#else /* CONFIG_SMP */ - +#if defined(CONFIG_SMP) if (emulate_local_timer_interrupt) { /* * this is the place where we send out inter-process * interrupts and let each CPU do its own profiling * and process accouting. - * - * Obviously we need to call local_timer_interrupt() for - * the current CPU too. */ panic("Not implemented yet!!!"); } diff -Nru linux/arch/mips/sibyte/sb1250/time.c.orig linux/arch/mips/sibyte/sb1250/time.c --- linux/arch/mips/sibyte/sb1250/time.c.orig Wed Jan 29 15:33:04 2003 +++ linux/arch/mips/sibyte/sb1250/time.c Wed May 28 17:39:44 2003 @@ -35,6 +35,7 @@ #include #include #include +#include #include #include @@ -102,7 +103,7 @@ int cpu = smp_processor_id(); int irq = K_INT_TIMER_0+cpu; - kstat.irqs[cpu][irq]++; + /* Reset the timer */ out64(M_SCD_TIMER_ENABLE|M_SCD_TIMER_MODE_CONTINUOUS, KSEG1 + A_SCD_TIMER_REGISTER(cpu, R_SCD_TIMER_CFG)); @@ -110,14 +111,10 @@ /* * CPU 0 handles the global timer interrupt job */ - if (cpu == 0) { + if (cpu == 0) ll_timer_interrupt(irq, regs); - } - - /* - * every CPU should do profiling and process accouting - */ - ll_local_timer_interrupt(irq, regs); + else + ll_local_timer_interrupt(irq, regs); } /* @@ -126,10 +123,35 @@ * In addition, since we use general timer 0 for system time, * we can get accurate intra-jiffy offset without calibration. */ +/* + * We assume the timer is 1 MHz (this is true at least according to spec). + */ + +spinlock_t system_timer_lock = SPIN_LOCK_UNLOCKED; +static unsigned long last_jiffie, last_ret; unsigned long sb1250_gettimeoffset(void) { - unsigned long count = - in64(KSEG1 + A_SCD_TIMER_REGISTER(0, R_SCD_TIMER_CNT)); + unsigned long count, ret; + + spin_lock(&system_timer_lock); + count = in64(KSEG1 + A_SCD_TIMER_REGISTER(0, R_SCD_TIMER_CNT)); + + db_assert(1000000/HZ >= count); + ret = 1000000/HZ - count; + if ((jiffies == last_jiffie) && (ret < last_ret)) { + /* + * Possibly timer interrupt just happened. Jiffies + * not updated yet. We can't trust the count value + * as it is because we would return non-monotonic + * values. Adjust by adding one more jiffie. + */ + ret += 1000000 / HZ; + } - return 1000000/HZ - count; - } + last_ret = ret; + last_jiffie = jiffies; + + spin_unlock(&system_timer_lock); + + return ret; +}