patch-2.2.0-pre1 linux/arch/i386/kernel/time.c
Next file: linux/arch/i386/kernel/traps.c
Previous file: linux/arch/i386/kernel/smp.c
Back to the patch index
Back to the overall index
- Lines: 238
- Date:
Mon Dec 28 10:52:01 1998
- Orig file:
v2.1.132/linux/arch/i386/kernel/time.c
- Orig date:
Tue Dec 22 14:16:54 1998
diff -u --recursive --new-file v2.1.132/linux/arch/i386/kernel/time.c linux/arch/i386/kernel/time.c
@@ -22,8 +22,10 @@
* ported from 2.0.35 Jumbo-9 by Michael Krause <m.krause@tu-harburg.de>).
* 1998-12-16 Andrea Arcangeli
* Fixed Jumbo-9 code in 2.1.131: do_gettimeofday was missing 1 jiffy
- * because was not accounting lost_ticks. I also removed some ugly
- * not needed global cli() and where needed I used a disable_irq(0).
+ * because was not accounting lost_ticks.
+ * 1998-12-24 Copyright (C) 1998 Andrea Arcangeli
+ * Fixed a xtime SMP race (we need the xtime_lock rw spinlock to
+ * serialize accesses to xtime/lost_ticks).
*/
/* What about the "updated NTP code" stuff in 2.0 time.c? It's not in
@@ -82,7 +84,9 @@
*/
static unsigned long fast_gettimeoffset_quotient=0;
-static unsigned long do_fast_gettimeoffset(void)
+extern rwlock_t xtime_lock;
+
+static inline unsigned long do_fast_gettimeoffset(void)
{
register unsigned long eax asm("ax");
register unsigned long edx asm("dx");
@@ -92,7 +96,6 @@
:"=a" (eax), "=d" (edx));
/* .. relative to previous jiffy (32 bits is enough) */
- edx = 0;
eax -= last_tsc_low; /* tsc_low delta */
/*
@@ -106,13 +109,20 @@
__asm__("mull %2"
:"=a" (eax), "=d" (edx)
- :"r" (fast_gettimeoffset_quotient),
- "0" (eax), "1" (edx));
+ :"g" (fast_gettimeoffset_quotient),
+ "0" (eax));
/* our adjusted time offset in microseconds */
- return edx + delay_at_last_interrupt;
+ return delay_at_last_interrupt + edx;
}
+#define TICK_SIZE tick
+
+/*
+ * Older CPU's don't have the rdtsc instruction..
+ */
+#if CPU < 586
+
/* This function must be called with interrupts disabled
* It was inspired by Steve McCanne's microtime-i386 for BSD. -- jrs
*
@@ -145,8 +155,6 @@
* comp.protocols.time.ntp!
*/
-#define TICK_SIZE tick
-
static unsigned long do_slow_gettimeoffset(void)
{
int count;
@@ -228,6 +236,12 @@
static unsigned long (*do_gettimeoffset)(void) = do_slow_gettimeoffset;
+#else
+
+#define do_gettimeoffset() do_fast_gettimeoffset()
+
+#endif
+
/*
* This version of gettimeofday has microsecond resolution
* and better than microsecond precision on fast x86 machines with TSC.
@@ -236,22 +250,31 @@
{
extern volatile unsigned long lost_ticks;
unsigned long flags;
+ unsigned long usec, sec;
- save_flags(flags); cli();
- *tv = xtime;
- tv->tv_usec += do_gettimeoffset();
- if (lost_ticks)
- tv->tv_usec += lost_ticks * (1000000/HZ);
- restore_flags(flags);
- while (tv->tv_usec >= 1000000) {
- tv->tv_usec -= 1000000;
- tv->tv_sec++;
+ read_lock_irqsave(&xtime_lock, flags);
+ usec = do_gettimeoffset();
+ {
+ unsigned long lost = lost_ticks;
+ if (lost)
+ usec += lost * (1000000 / HZ);
+ }
+ sec = xtime.tv_sec;
+ usec += xtime.tv_usec;
+ read_unlock_irqrestore(&xtime_lock, flags);
+
+ while (usec >= 1000000) {
+ usec -= 1000000;
+ sec++;
}
+
+ tv->tv_sec = sec;
+ tv->tv_usec = usec;
}
void do_settimeofday(struct timeval *tv)
{
- cli();
+ write_lock_irq(&xtime_lock);
/* This is revolting. We need to set the xtime.tv_usec
* correctly. However, the value in this location is
* is value at the last tick.
@@ -269,7 +292,7 @@
time_state = TIME_BAD;
time_maxerror = MAXPHASE;
time_esterror = MAXPHASE;
- sti();
+ write_unlock_irq(&xtime_lock);
}
/*
@@ -344,7 +367,7 @@
* timer_interrupt() needs to keep up the real-time clock,
* as well as call the "do_timer()" routine every clocktick
*/
-static inline void timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+static inline void do_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
do_timer(regs);
/*
@@ -373,13 +396,6 @@
else
last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */
}
-#if 0
- /* As we return to user mode fire off the other CPU schedulers.. this is
- basically because we don't yet share IRQ's around. This message is
- rigged to be safe on the 386 - basically it's a hack, so don't look
- closely for now.. */
- smp_message_pass(MSG_ALL_BUT_SELF, MSG_RESCHEDULE, 0L, 0);
-#endif
#ifdef CONFIG_MCA
if( MCA_bus ) {
@@ -398,37 +414,56 @@
#endif
}
+static int use_tsc = 0;
+
/*
* This is the same as the above, except we _also_ save the current
* Time Stamp Counter value at the time of the timer interrupt, so that
* we later on can estimate the time of day more exactly.
*/
-static void pentium_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+static void timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
int count;
- /* 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
+ * 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);
+
+ 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 */
- __asm__("rdtsc" : "=a" (last_tsc_low) : : "edx");
+ /* read Pentium cycle counter */
+ __asm__("rdtsc" : "=a" (last_tsc_low) : : "edx");
- outb_p(0x00, 0x43); /* latch the count ASAP */
+ outb_p(0x00, 0x43); /* latch the count ASAP */
- count = inb_p(0x40); /* read the latched count */
- count |= inb(0x40) << 8;
+ 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;
+ count = ((LATCH-1) - count) * TICK_SIZE;
+ delay_at_last_interrupt = (count + LATCH/2) / LATCH;
+ }
- timer_interrupt(irq, NULL, regs);
+ do_timer_interrupt(irq, NULL, regs);
+
+ write_unlock(&xtime_lock);
+
}
/* Converts Gregorian date to seconds since 1970-01-01 00:00:00.
@@ -599,9 +634,11 @@
* smart. See arch/i386/kernel/apm.c.
*/
if (boot_cpu_data.x86_capability & X86_FEATURE_TSC) {
+#ifndef do_gettimeoffset
do_gettimeoffset = do_fast_gettimeoffset;
+#endif
do_get_fast_time = do_gettimeofday;
- irq0.handler = pentium_timer_interrupt;
+ use_tsc = 1;
fast_gettimeoffset_quotient = calibrate_tsc();
/* report CPU clock rate in Hz.
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov