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

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