patch-1.3.31 linux/arch/i386/kernel/time.c

Next file: linux/arch/i386/kernel/trampoline.S
Previous file: linux/arch/i386/kernel/smp.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v1.3.30/linux/arch/i386/kernel/time.c linux/arch/i386/kernel/time.c
@@ -26,104 +26,58 @@
 
 #define TIMER_IRQ 0
 
-static int set_rtc_mmss(unsigned long);
+/* Cycle counter value at the previous timer interrupt.. */
+static unsigned long long last_timer_cc = 0;
+static unsigned long long init_timer_cc = 0;
 
-/*
- * timer_interrupt() needs to keep up the real-time clock,
- * as well as call the "do_timer()" routine every clocktick
- */
-static void timer_interrupt(int irq, struct pt_regs * regs)
+static unsigned long do_fast_gettimeoffset(void)
 {
-	/* last time the cmos clock got updated */
-	static long last_rtc_update=0;
-
-	do_timer(regs);
+	unsigned long time_low, time_high;
+	unsigned long quotient, remainder;
 
+	/* Get last timer tick in absolute kernel time */
+	__asm__("subl %2,%0\n\t"
+		"sbbl %3,%1"
+		:"=r" (time_low), "=r" (time_high)
+		:"m" (*(0+(long *)&init_timer_cc)),
+		 "m" (*(1+(long *)&init_timer_cc)),
+		 "0" (*(0+(long *)&last_timer_cc)),
+		 "1" (*(1+(long *)&last_timer_cc)));
 	/*
-	 * If we have an externally synchronized Linux clock, then update
-	 * CMOS clock accordingly every ~11 minutes. Set_rtc_mmss() has to be
-	 * called as close as possible to 500 ms before the new second starts.
+	 * Divide the 64-bit time with the 32-bit jiffy counter,
+	 * getting the quotient in clocks.
+	 *
+	 * Giving quotient = "average internal clocks per jiffy"
 	 */
-	if (time_state != TIME_BAD && 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
-	    last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */
-}
+	__asm__("divl %2"
+		:"=a" (quotient), "=d" (remainder)
+		:"r" (jiffies),
+		 "0" (time_low), "1" (time_high));
+
+	/* Read the time counter */
+	__asm__(".byte 0x0f,0x31"
+		:"=a" (time_low), "=d" (time_high));
 
-/* Converts Gregorian date to seconds since 1970-01-01 00:00:00.
- * Assumes input in normal date format, i.e. 1980-12-31 23:59:59
- * => year=1980, mon=12, day=31, hour=23, min=59, sec=59.
- *
- * [For the Julian calendar (which was used in Russia before 1917,
- * Britain & colonies before 1752, anywhere else before 1582,
- * and is still in use by some communities) leave out the
- * -year/100+year/400 terms, and add 10.]
- *
- * This algorithm was first published by Gauss (I think).
- *
- * WARNING: this function will overflow on 2106-02-07 06:28:16 on
- * machines were long is 32-bit! (However, as time_t is signed, we
- * will already get problems at other places on 2038-01-19 03:14:08)
- */
-static inline unsigned long mktime(unsigned int year, unsigned int mon,
-	unsigned int day, unsigned int hour,
-	unsigned int min, unsigned int sec)
-{
-	if (0 >= (int) (mon -= 2)) {	/* 1..12 -> 11,12,1..10 */
-		mon += 12;	/* Puts Feb last since it has leap day */
-		year -= 1;
-	}
-	return (((
-	    (unsigned long)(year/4 - year/100 + year/400 + 367*mon/12 + day) +
-	      year*365 - 719499
-	    )*24 + hour /* now have hours */
-	   )*60 + min /* now have minutes */
-	  )*60 + sec; /* finally seconds */
-}
+	/* .. relative to previous jiffy (32 bits is enough) */
+	time_low -= (unsigned long) last_timer_cc;
 
-void time_init(void)
-{
-	unsigned int year, mon, day, hour, min, sec;
-	int i;
+	/*
+	 * Time offset = (1000000/HZ * remainder) / quotient.
+	 */
+	__asm__("mull %1\n\t"
+		"divl %2"
+		:"=a" (quotient), "=d" (remainder)
+		:"r" (quotient),
+		 "0" (time_low), "1" (1000000/HZ));
 
-	/* The Linux interpretation of the CMOS clock register contents:
-	 * When the Update-In-Progress (UIP) flag goes from 1 to 0, the
-	 * RTC registers show the second which has precisely just started.
-	 * Let's hope other operating systems interpret the RTC the same way.
+	/*
+	 * Due to rounding errors (and jiffies inconsistencies),
+	 * we need to check the result so that we'll get a timer
+	 * that is monotonous.
 	 */
-	/* read RTC exactly on falling edge of update flag */
-	for (i = 0 ; i < 1000000 ; i++)	/* may take up to 1 second... */
-		if (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP)
-			break;
-	for (i = 0 ; i < 1000000 ; i++)	/* must try at least 2.228 ms */
-		if (!(CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP))
-			break;
-	do { /* Isn't this overkill ? UIP above should guarantee consistency */
-		sec = CMOS_READ(RTC_SECONDS);
-		min = CMOS_READ(RTC_MINUTES);
-		hour = CMOS_READ(RTC_HOURS);
-		day = CMOS_READ(RTC_DAY_OF_MONTH);
-		mon = CMOS_READ(RTC_MONTH);
-		year = CMOS_READ(RTC_YEAR);
-	} while (sec != CMOS_READ(RTC_SECONDS));
-	if (!(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY) || RTC_ALWAYS_BCD)
-	  {
-	    BCD_TO_BIN(sec);
-	    BCD_TO_BIN(min);
-	    BCD_TO_BIN(hour);
-	    BCD_TO_BIN(day);
-	    BCD_TO_BIN(mon);
-	    BCD_TO_BIN(year);
-	  }
-	if ((year += 1900) < 1970)
-		year += 100;
-	xtime.tv_sec = mktime(year, mon, day, hour, min, sec);
-	xtime.tv_usec = 0;
-	if (request_irq(TIMER_IRQ, timer_interrupt, 0, "timer") != 0)
-		panic("Could not allocate timer IRQ!");
+	if (quotient >= 1000000/HZ)
+		quotient = 1000000/HZ-1;
+	return quotient;
 }
 
 /* This function must be called with interrupts disabled 
@@ -160,7 +114,7 @@
 
 #define TICK_SIZE tick
 
-static inline unsigned long do_gettimeoffset(void)
+static unsigned long do_slow_gettimeoffset(void)
 {
 	int count;
 	unsigned long offset = 0;
@@ -181,6 +135,8 @@
 	return offset + count;
 }
 
+static unsigned long (*do_gettimeoffset)(void) = do_slow_gettimeoffset;
+
 /*
  * This version of gettimeofday has near microsecond resolution.
  */
@@ -279,4 +235,134 @@
 	CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT);
 
 	return retval;
+}
+
+/* last time the cmos clock got updated */
+static long last_rtc_update = 0;
+
+/*
+ * 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, struct pt_regs * regs)
+{
+	do_timer(regs);
+
+	/*
+	 * If we have an externally synchronized Linux clock, then update
+	 * CMOS clock accordingly every ~11 minutes. Set_rtc_mmss() has to be
+	 * called as close as possible to 500 ms before the new second starts.
+	 */
+	if (time_state != TIME_BAD && 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
+	    last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */
+	/* 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 its a hack, so don't look
+	   closely for now.. */
+	smp_message_pass(MSG_ALL_BUT_SELF, MSG_RESCHEDULE, 0L, 0); 
+	    
+}
+
+/*
+ * This is the same as the above, except we _also_ save the current
+ * cycle 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, struct pt_regs * regs)
+{
+	/* read Pentium cycle counter */
+	__asm__(".byte 0x0f,0x31"
+		:"=a" (((unsigned long *) &last_timer_cc)[0]),
+		 "=d" (((unsigned long *) &last_timer_cc)[1]));
+	timer_interrupt(irq, regs);
+}
+
+/* Converts Gregorian date to seconds since 1970-01-01 00:00:00.
+ * Assumes input in normal date format, i.e. 1980-12-31 23:59:59
+ * => year=1980, mon=12, day=31, hour=23, min=59, sec=59.
+ *
+ * [For the Julian calendar (which was used in Russia before 1917,
+ * Britain & colonies before 1752, anywhere else before 1582,
+ * and is still in use by some communities) leave out the
+ * -year/100+year/400 terms, and add 10.]
+ *
+ * This algorithm was first published by Gauss (I think).
+ *
+ * WARNING: this function will overflow on 2106-02-07 06:28:16 on
+ * machines were long is 32-bit! (However, as time_t is signed, we
+ * will already get problems at other places on 2038-01-19 03:14:08)
+ */
+static inline unsigned long mktime(unsigned int year, unsigned int mon,
+	unsigned int day, unsigned int hour,
+	unsigned int min, unsigned int sec)
+{
+	if (0 >= (int) (mon -= 2)) {	/* 1..12 -> 11,12,1..10 */
+		mon += 12;	/* Puts Feb last since it has leap day */
+		year -= 1;
+	}
+	return (((
+	    (unsigned long)(year/4 - year/100 + year/400 + 367*mon/12 + day) +
+	      year*365 - 719499
+	    )*24 + hour /* now have hours */
+	   )*60 + min /* now have minutes */
+	  )*60 + sec; /* finally seconds */
+}
+
+void time_init(void)
+{
+	void (*irq_handler)(int, struct pt_regs *);
+	unsigned int year, mon, day, hour, min, sec;
+	int i;
+
+	/* The Linux interpretation of the CMOS clock register contents:
+	 * When the Update-In-Progress (UIP) flag goes from 1 to 0, the
+	 * RTC registers show the second which has precisely just started.
+	 * Let's hope other operating systems interpret the RTC the same way.
+	 */
+	/* read RTC exactly on falling edge of update flag */
+	for (i = 0 ; i < 1000000 ; i++)	/* may take up to 1 second... */
+		if (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP)
+			break;
+	for (i = 0 ; i < 1000000 ; i++)	/* must try at least 2.228 ms */
+		if (!(CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP))
+			break;
+	do { /* Isn't this overkill ? UIP above should guarantee consistency */
+		sec = CMOS_READ(RTC_SECONDS);
+		min = CMOS_READ(RTC_MINUTES);
+		hour = CMOS_READ(RTC_HOURS);
+		day = CMOS_READ(RTC_DAY_OF_MONTH);
+		mon = CMOS_READ(RTC_MONTH);
+		year = CMOS_READ(RTC_YEAR);
+	} while (sec != CMOS_READ(RTC_SECONDS));
+	if (!(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY) || RTC_ALWAYS_BCD)
+	  {
+	    BCD_TO_BIN(sec);
+	    BCD_TO_BIN(min);
+	    BCD_TO_BIN(hour);
+	    BCD_TO_BIN(day);
+	    BCD_TO_BIN(mon);
+	    BCD_TO_BIN(year);
+	  }
+	if ((year += 1900) < 1970)
+		year += 100;
+	xtime.tv_sec = mktime(year, mon, day, hour, min, sec);
+	xtime.tv_usec = 0;
+
+	/* If we have the CPU hardware time counters, use them */
+	irq_handler = timer_interrupt;	
+	if (x86_capability & 16) {
+		irq_handler = pentium_timer_interrupt;
+		do_gettimeoffset = do_fast_gettimeoffset;
+		/* read Pentium cycle counter */
+		__asm__(".byte 0x0f,0x31"
+			:"=a" (((unsigned long *) &init_timer_cc)[0]),
+			 "=d" (((unsigned long *) &init_timer_cc)[1]));
+	}
+	if (request_irq(TIMER_IRQ, irq_handler, 0, "timer") != 0)
+		panic("Could not allocate timer IRQ!");
 }

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov with Sam's (original) version
of this