changeset 2992:e77328e68298

6266961 Jitter seen in cyclics on x86 due to trunc. error in apic_timer_reprogram 6486473 APIC timer divisor register only initialized on CPU0
author dmick
date Thu, 26 Oct 2006 17:11:42 -0700
parents 4b13d6c49c6b
children 32f53d0021a5
files usr/src/uts/i86pc/io/pcplusmp/apic.c usr/src/uts/i86pc/io/pcplusmp/apic.h
diffstat 2 files changed, 47 insertions(+), 35 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/uts/i86pc/io/pcplusmp/apic.c	Thu Oct 26 16:44:53 2006 -0700
+++ b/usr/src/uts/i86pc/io/pcplusmp/apic.c	Thu Oct 26 17:11:42 2006 -0700
@@ -472,7 +472,10 @@
 static	lock_t	apic_gethrtime_lock;
 volatile int	apic_hrtime_stamp = 0;
 volatile hrtime_t apic_nsec_since_boot = 0;
-static uint_t apic_hertz_count, apic_nsec_per_tick;
+static uint_t apic_hertz_count;
+
+uint64_t apic_ticks_per_SFnsecs;	/* # of ticks in SF nsecs */
+
 static hrtime_t apic_nsec_max;
 
 static	hrtime_t	apic_last_hrtime = 0;
@@ -639,6 +642,7 @@
 /* Patchable global variables. */
 int		apic_kmdb_on_nmi = 0;		/* 0 - no, 1 - yes enter kmdb */
 int		apic_debug_mps_id = 0;		/* 1 - print MPS ID strings */
+uint32_t	apic_divide_reg_init = 0;	/* 0 - divide by 2 */
 
 /*
  * ACPI definitions
@@ -2229,7 +2233,7 @@
 
 	elapsed_ticks = apic_hertz_count - countval;
 
-	curr_timeval = elapsed_ticks * apic_nsec_per_tick;
+	curr_timeval = APIC_TICKS_TO_NSECS(elapsed_ticks);
 	temp = apic_nsec_since_boot + curr_timeval;
 
 	if (apic_hrtime_stamp != old_hrtime_stamp) {	/* got an interrupt */
@@ -2595,6 +2599,7 @@
 		}
 	}
 
+	apicadr[APIC_DIVIDE_REG] = apic_divide_reg_init;
 	return (PSM_SUCCESS);
 }
 
@@ -2657,6 +2662,7 @@
 	return (irq);
 }
 
+
 /*
  * Return the number of APIC clock ticks elapsed for 8245 to decrement
  * (APIC_TIME_COUNT + pit_ticks_adj) ticks.
@@ -2731,53 +2737,44 @@
 {
 
 	uint_t		apic_ticks = 0;
-	uint_t		pit_time;
+	uint_t		pit_ticks;
 	int		ret;
 	uint16_t	pit_ticks_adj;
 	static int	firsttime = 1;
 
 	if (firsttime) {
-		/* first time calibrate */
-
-		apicadr[APIC_DIVIDE_REG] = 0x0;
-		apicadr[APIC_INIT_COUNT] = APIC_MAXVAL;
-
-		/* set periodic interrupt based on CLKIN */
-		apicadr[APIC_LOCAL_TIMER] =
-		    (apic_clkvect + APIC_BASE_VECT) | AV_TIME;
-		tenmicrosec();
-
+		/* first time calibrate on CPU0 only */
+
+		apicadr[APIC_DIVIDE_REG] = apic_divide_reg_init;
+		apicadr[APIC_INIT_COUNT] = APIC_MAXVAL;	/* start counting */
 		apic_ticks = apic_calibrate(apicadr, &pit_ticks_adj);
 
-		apicadr[APIC_LOCAL_TIMER] =
-		    (apic_clkvect + APIC_BASE_VECT) | AV_MASK;
-		/*
-		 * pit time is the amount of real time (in nanoseconds ) it took
-		 * the 8254 to decrement (APIC_TIME_COUNT + pit_ticks_adj) ticks
-		 */
-		pit_time = ((longlong_t)(APIC_TIME_COUNT +
-		    pit_ticks_adj) * NANOSEC) / PIT_HZ;
+		/* total number of PIT ticks corresponding to apic_ticks */
+		pit_ticks = APIC_TIME_COUNT + pit_ticks_adj;
 
 		/*
 		 * Determine the number of nanoseconds per APIC clock tick
 		 * and then determine how many APIC ticks to interrupt at the
 		 * desired frequency
+		 * apic_ticks / (pitticks / PIT_HZ) = apic_ticks_per_s
+		 * (apic_ticks * PIT_HZ) / pitticks = apic_ticks_per_s
+		 * apic_ticks_per_ns = (apic_ticks * PIT_HZ) / (pitticks * 10^9)
+		 * apic_ticks_per_SFns =
+		 *   (SF * apic_ticks * PIT_HZ) / (pitticks * 10^9)
 		 */
-		apic_nsec_per_tick = pit_time / apic_ticks;
-		if (apic_nsec_per_tick == 0)
-			apic_nsec_per_tick = 1;
+		apic_ticks_per_SFnsecs =
+		    ((SF * apic_ticks * PIT_HZ) /
+		    ((uint64_t)pit_ticks * NANOSEC));
 
 		/* the interval timer initial count is 32 bit max */
-		apic_nsec_max = (hrtime_t)apic_nsec_per_tick * APIC_MAXVAL;
+		apic_nsec_max = APIC_TICKS_TO_NSECS(APIC_MAXVAL);
 		firsttime = 0;
 	}
 
 	if (hertz != 0) {
 		/* periodic */
 		apic_nsec_per_intr = NANOSEC / hertz;
-		apic_hertz_count = (longlong_t)apic_nsec_per_intr /
-		    apic_nsec_per_tick;
-		apic_sample_factor_redistribution = hertz + 1;
+		apic_hertz_count = APIC_NSECS_TO_TICKS(apic_nsec_per_intr);
 	}
 
 	apic_int_busy_mark = (apic_int_busy_mark *
@@ -2792,7 +2789,7 @@
 		if (!apic_oneshot_enable)
 			return (0);
 		apic_oneshot = 1;
-		ret = (int)apic_nsec_per_tick;
+		ret = (int)APIC_TICKS_TO_NSECS(1);
 	} else {
 		/* program the local APIC to interrupt at the given frequency */
 		apicadr[APIC_INIT_COUNT] = apic_hertz_count;
@@ -4862,6 +4859,7 @@
 {
 	hrtime_t now;
 	uint_t ticks;
+	int64_t	delta;
 
 	/*
 	 * We should be called from high PIL context (CBE_HIGH_PIL),
@@ -4870,19 +4868,20 @@
 
 	if (!apic_oneshot) {
 		/* time is the interval for periodic mode */
-		ticks = (uint_t)((time) / apic_nsec_per_tick);
+		ticks = APIC_NSECS_TO_TICKS(time);
 	} else {
 		/* one shot mode */
 
 		now = gethrtime();
-
-		if (time <= now) {
+		delta = time - now;
+
+		if (delta <= 0) {
 			/*
 			 * requested to generate an interrupt in the past
 			 * generate an interrupt as soon as possible
 			 */
 			ticks = apic_min_timer_ticks;
-		} else if ((time - now) > apic_nsec_max) {
+		} else if (delta > apic_nsec_max) {
 			/*
 			 * requested to generate an interrupt at a time
 			 * further than what we are capable of. Set to max
@@ -4894,9 +4893,9 @@
 			cmn_err(CE_CONT, "apic_timer_reprogram, request at"
 			    "  %lld  too far in future, current time"
 			    "  %lld \n", time, now);
-#endif	/* DEBUG */
+#endif
 		} else
-			ticks = (uint_t)((time - now) / apic_nsec_per_tick);
+			ticks = APIC_NSECS_TO_TICKS(delta);
 	}
 
 	if (ticks < apic_min_timer_ticks)
--- a/usr/src/uts/i86pc/io/pcplusmp/apic.h	Thu Oct 26 16:44:53 2006 -0700
+++ b/usr/src/uts/i86pc/io/pcplusmp/apic.h	Thu Oct 26 17:11:42 2006 -0700
@@ -553,6 +553,19 @@
 #define	PSMGI_INTRBY_IRQ	0x8000	/* IRQ passed.  no xlate needed */
 #define	PSMGI_INTRBY_FLAGS	0x8000	/* Mask for this flag */
 
+/*
+ * Use scaled-fixed-point arithmetic to calculate apic ticks.
+ * Round when dividing (by adding half of divisor to dividend)
+ * for one extra bit of precision.
+ */
+
+#define	SF	(1ULL<<20)		/* Scaling Factor: scale by 2^20 */
+#define	APIC_TICKS_TO_NSECS(ticks)	((((int64_t)(ticks) * SF) + \
+					apic_ticks_per_SFnsecs / 2) / \
+					apic_ticks_per_SFnsecs);
+#define	APIC_NSECS_TO_TICKS(nsecs)	(((int64_t)(nsecs) * \
+					apic_ticks_per_SFnsecs + (SF/2)) / SF)
+
 #ifdef	__cplusplus
 }
 #endif