Subject: Fixing sgimips clock behaviour
To: None <port-sgimips@netbsd.org, port-mips@netbsd.org>
From: Rafal Boni <rafal@attbi.com>
List: port-sgimips
Date: 02/26/2002 21:14:22
So I finally got some time to try and figure out why the clock on my sgi
was always being stepped by ntpd and have a fix for the problem.  

The problem (as suggested by Jason a while ago and pounded into my head
when browsing through a copy of See Mips Run) is that rather than sched-
uling the next clock interrupt a fixed interval from *when the last one
was supposed to occurr*, we scheduled the next clock interrupt a fixed
interval from *when we serviced* the current one.

The following patch fixes it, but I'm still not sure what the right
thing to do is if the interrupt is so delayed that we'll miss the next
one -- I currently simply schedule the next clock interrupt the fixed
time from *now* to avoid complexity in figuring out how far we'd fallen
behind and adding in the right fugde factor.

I also need to figure out where to shove the new globals needed to keep
state between clock.c and ipNN.c and need to fix up ip32.c similarly.
With this patch, I've rebuild a complete userland on my Challenge S
without any clock-related problems, whereas before a complete user-
land build would cause ntpd to step my clock at least half-a-dozen
times.  Things look similarly good when the machine was sitting idle.
I haven't ran this for a real long time, though...

Comments?
--rafal

BTW, Jason: At least Algor also has this problem, haven't looked at the
other mips ports.

Patch follows, there's probably some fuzz as I have other local changes
to at least ip22.c which I leave out for the purposes of this discussion.

Index: sgimips/clock.c
===================================================================
RCS file: /cvsroot/syssrc/sys/arch/sgimips/sgimips/clock.c,v
retrieving revision 1.3
diff -u -r1.3 clock.c
--- sgimips/clock.c	2001/10/10 13:24:47	1.3
+++ sgimips/clock.c	2002/02/27 01:40:12
@@ -48,11 +48,15 @@
 #include <sys/kernel.h>
 #include <sys/systm.h>
 
+#include <mips/locore.h>
 #include <dev/clock_subr.h>
 #include <sgimips/sgimips/clockvar.h>
 
 #define MINYEAR 2001 /* "today" */
 
+extern u_int32_t ticks_per_hz;
+extern u_int32_t next_clk_intr;
+
 static struct device *clockdev;
 static const struct clockfns *clockfns;
 static int clockinitted;
@@ -103,9 +107,21 @@
 void
 cpu_initclocks()
 {
-
 	if (clockfns == NULL)
 		panic("cpu_initclocks: clock device not attached");
+
+	next_clk_intr = mips3_cp0_count_read() + ticks_per_hz;
+	mips3_cp0_compare_write(next_clk_intr);
+
+	tick = 1000000 / hz;	/* number of microseconds between interrupts */
+	tickfix = 1000000 - (hz * tick);
+	if (tickfix) {
+		int ftp;
+
+		ftp = min(ffs(tickfix), ffs(hz));
+		tickfix >>= (ftp - 1);
+		tickfixinterval = hz >> (ftp - 1);
+        }
 
 	(*clockfns->cf_init)(clockdev);
 }
Index: sgimips/ip22.c
===================================================================
RCS file: /cvsroot/syssrc/sys/arch/sgimips/sgimips/ip22.c,v
retrieving revision 1.7
diff -u -r1.7 ip22.c
--- sgimips/ip22.c	2001/11/14 18:15:35	1.7
+++ sgimips/ip22.c	2002/02/27 01:40:12
@@ -43,6 +43,10 @@
 
 #include <mips/cache.h>
 
+u_int32_t ticks_per_hz;
+u_int32_t next_clk_intr;
+u_int32_t missed_clk_intrs;
+
 static struct evcnt mips_int5_evcnt =
     EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, "mips", "int 5 (clock)");
 
@@ -50,11 +54,8 @@
 static u_int32_t iocreset;	/* IOC reset register: read-only */
 
 static unsigned long last_clk_intr;
-
-static unsigned long ticks_per_hz;
 static unsigned long ticks_per_usec;
 
-
 void		ip22_init(void);
 void 		ip22_bus_reset(void);
 int 		ip22_local0_intr(void);
@@ -71,6 +72,9 @@
 extern void	ip22_sdcache_enable(void);
 extern void	ip22_sdcache_disable(void);
 
+/* clock.c */
+extern u_int32_t next_clk_intr;
+
 void 
 ip22_init(void)
 {
@@ -195,6 +202,7 @@
 	u_int32_t pc;
 	u_int32_t ipending;
 {
+	u_int32_t newcnt;
 	struct clockframe cf;
 
 	/* Tickle Indy/I2 MC watchdog timer */ 
@@ -202,7 +210,21 @@
 
 	if (ipending & MIPS_INT_MASK_5) {
 		last_clk_intr = mips3_cp0_count_read();
-		mips3_cp0_compare_write(last_clk_intr + ticks_per_hz);
+
+		next_clk_intr += ticks_per_hz;
+		mips3_cp0_compare_write(next_clk_intr);
+		newcnt = mips3_cp0_count_read();
+
+		/* 
+		 * Missed one or more clock interrupts, so let's start 
+		 * counting again from the current value.
+		 */
+		if ((next_clk_intr - newcnt) & 0x80000000) {
+		    missed_clk_intrs++;
+
+		    next_clk_intr = newcnt + ticks_per_hz;
+		    mips3_cp0_compare_write(next_clk_intr);
+		}
 
 		cf.pc = pc;
 		cf.sr = status;
@@ -210,7 +232,10 @@
 		hardclock(&cf);
 		mips_int5_evcnt.ev_count++;
 
+		/* Re-enable clock interrupts. */
 		cause &= ~MIPS_INT_MASK_5;
+		_splset(MIPS_SR_INT_IE |
+		    ((status & ~cause) & MIPS_HARD_INT_MASK));
 	}
 
 	if (ipending & MIPS_INT_MASK_0) {
----
Rafal Boni                                                     rafal@attbi.com