svn commit: r202387 - in head/sys: amd64/amd64 amd64/include amd64/isa conf i386/i386 i386/include i386/isa pc98/cbus

Attilio Rao attilio at FreeBSD.org
Fri Jan 15 16:04:30 UTC 2010


Author: attilio
Date: Fri Jan 15 16:04:30 2010
New Revision: 202387
URL: http://svn.freebsd.org/changeset/base/202387

Log:
  Handling all the three clocks (hardclock, softclock, profclock) with the
  LAPIC may lead to aliasing for softclock and profclock because frequencies
  are sized in order to fit mainly hardclock.
  atrtc used to take care of the softclock and profclock and it does still
  do, if the LAPIC can't handle the clocks properly.
  
  Revert the change when the LAPIC started taking charge of all three of
  them and let atrtc handle softclock and profclock if not explicitly
  requested. Such request can be made setting != 0 the new tunable
  machdep.lapic_allclocks or if the new device ATPIC is not present
  within the i386 kernel config (atrtc is linked to atpic presence).
  
  Diagnosed by:	Sandvine Incorporated
  Reviewed by:	jhb, emaste
  Sponsored by:	Sandvine Incorporated
  MFC:		3 weeks

Modified:
  head/sys/amd64/amd64/local_apic.c
  head/sys/amd64/include/apicvar.h
  head/sys/amd64/isa/clock.c
  head/sys/conf/options.i386
  head/sys/i386/i386/local_apic.c
  head/sys/i386/include/apicvar.h
  head/sys/i386/isa/clock.c
  head/sys/pc98/cbus/clock.c

Modified: head/sys/amd64/amd64/local_apic.c
==============================================================================
--- head/sys/amd64/amd64/local_apic.c	Fri Jan 15 16:01:22 2010	(r202386)
+++ head/sys/amd64/amd64/local_apic.c	Fri Jan 15 16:04:30 2010	(r202387)
@@ -160,6 +160,9 @@ static uint32_t	lvt_mode(struct lapic *l
 
 struct pic lapic_pic = { .pic_resume = lapic_resume };
 
+static int lapic_allclocks;
+TUNABLE_INT("machdep.lapic_allclocks", &lapic_allclocks);
+
 static uint32_t
 lvt_mode(struct lapic *la, u_int pin, uint32_t value)
 {
@@ -415,10 +418,11 @@ lapic_disable_pmc(void)
 /*
  * Called by cpu_initclocks() on the BSP to setup the local APIC timer so
  * that it can drive hardclock, statclock, and profclock.  This function
- * returns true if it is able to use the local APIC timer to drive the
- * clocks and false if it is not able.
+ * returns a positive integer if it is convenient to use the local APIC
+ * for all the clocks, a negative integer if it is convenient to use the
+ * local APIC only for the hardclock and 0 if none of them can be handled.
  */
-int
+enum lapic_clock
 lapic_setup_clock(void)
 {
 	u_long value;
@@ -426,10 +430,10 @@ lapic_setup_clock(void)
 
 	/* Can't drive the timer without a local APIC. */
 	if (lapic == NULL)
-		return (0);
+		return (LAPIC_CLOCK_NONE);
 
 	if (resource_int_value("apic", 0, "clock", &i) == 0 && i == 0)
-		return (0);
+		return (LAPIC_CLOCK_NONE);
 
 	/* Start off with a divisor of 2 (power on reset default). */
 	lapic_timer_divisor = 2;
@@ -461,19 +465,27 @@ lapic_setup_clock(void)
 	 * (and profhz) run at hz.  If 'hz' is below 1500 but above
 	 * 750, then we let the lapic timer run at 2 * 'hz'.  If 'hz'
 	 * is below 750 then we let the lapic timer run at 4 * 'hz'.
+	 *
+	 * Please note that stathz and profhz are set only if all the
+	 * clocks are handled through the local APIC.
 	 */
-	if (hz >= 1500)
+	if (lapic_allclocks != 0) {
+		if (hz >= 1500)
+			lapic_timer_hz = hz;
+		else if (hz >= 750)
+			lapic_timer_hz = hz * 2;
+		else
+			lapic_timer_hz = hz * 4;
+	} else
 		lapic_timer_hz = hz;
-	else if (hz >= 750)
-		lapic_timer_hz = hz * 2;
-	else
-		lapic_timer_hz = hz * 4;
-	if (lapic_timer_hz < 128)
-		stathz = lapic_timer_hz;
-	else
-		stathz = lapic_timer_hz / (lapic_timer_hz / 128);
-	profhz = lapic_timer_hz;
 	lapic_timer_period = value / lapic_timer_hz;
+	if (lapic_allclocks != 0) {
+		if (lapic_timer_hz < 128)
+			stathz = lapic_timer_hz;
+		else
+			stathz = lapic_timer_hz / (lapic_timer_hz / 128);
+		profhz = lapic_timer_hz;
+	}
 
 	/*
 	 * Start up the timer on the BSP.  The APs will kick off their
@@ -481,7 +493,7 @@ lapic_setup_clock(void)
 	 */
 	lapic_timer_periodic(lapic_timer_period);
 	lapic_timer_enable_intr();
-	return (1);
+	return (lapic_allclocks == 0 ? LAPIC_CLOCK_HARDCLOCK : LAPIC_CLOCK_ALL);
 }
 
 void
@@ -784,20 +796,23 @@ lapic_handle_timer(struct trapframe *fra
 		else
 			hardclock_cpu(TRAPF_USERMODE(frame));
 	}
+	if (lapic_allclocks != 0) {
 
-	/* Fire statclock at stathz. */
-	la->la_stat_ticks += stathz;
-	if (la->la_stat_ticks >= lapic_timer_hz) {
-		la->la_stat_ticks -= lapic_timer_hz;
-		statclock(TRAPF_USERMODE(frame));
-	}
+		/* Fire statclock at stathz. */
+		la->la_stat_ticks += stathz;
+		if (la->la_stat_ticks >= lapic_timer_hz) {
+			la->la_stat_ticks -= lapic_timer_hz;
+			statclock(TRAPF_USERMODE(frame));
+		}
 
-	/* Fire profclock at profhz, but only when needed. */
-	la->la_prof_ticks += profhz;
-	if (la->la_prof_ticks >= lapic_timer_hz) {
-		la->la_prof_ticks -= lapic_timer_hz;
-		if (profprocs != 0)
-			profclock(TRAPF_USERMODE(frame), TRAPF_PC(frame));
+		/* Fire profclock at profhz, but only when needed. */
+		la->la_prof_ticks += profhz;
+		if (la->la_prof_ticks >= lapic_timer_hz) {
+			la->la_prof_ticks -= lapic_timer_hz;
+			if (profprocs != 0)
+				profclock(TRAPF_USERMODE(frame),
+				    TRAPF_PC(frame));
+		}
 	}
 	critical_exit();
 }

Modified: head/sys/amd64/include/apicvar.h
==============================================================================
--- head/sys/amd64/include/apicvar.h	Fri Jan 15 16:01:22 2010	(r202386)
+++ head/sys/amd64/include/apicvar.h	Fri Jan 15 16:04:30 2010	(r202387)
@@ -157,6 +157,12 @@
 #define	APIC_BUS_PCI		2
 #define	APIC_BUS_MAX		APIC_BUS_PCI
 
+enum lapic_clock {
+	LAPIC_CLOCK_NONE,
+	LAPIC_CLOCK_HARDCLOCK,
+	LAPIC_CLOCK_ALL
+};
+
 /*
  * An APIC enumerator is a psuedo bus driver that enumerates APIC's including
  * CPU's and I/O APIC's.
@@ -224,7 +230,7 @@ int	lapic_set_lvt_triggermode(u_int apic
 	    enum intr_trigger trigger);
 void	lapic_set_tpr(u_int vector);
 void	lapic_setup(int boot);
-int	lapic_setup_clock(void);
+enum lapic_clock	lapic_setup_clock(void);
 
 #endif /* !LOCORE */
 #endif /* _MACHINE_APICVAR_H_ */

Modified: head/sys/amd64/isa/clock.c
==============================================================================
--- head/sys/amd64/isa/clock.c	Fri Jan 15 16:01:22 2010	(r202386)
+++ head/sys/amd64/isa/clock.c	Fri Jan 15 16:04:30 2010	(r202387)
@@ -91,7 +91,7 @@ static	u_int32_t i8254_offset;
 static	int	(*i8254_pending)(struct intsrc *);
 static	int	i8254_ticked;
 static	int	using_atrtc_timer;
-static	int	using_lapic_timer;
+static	enum lapic_clock using_lapic_timer = LAPIC_CLOCK_NONE;
 
 /* Values for timerX_state: */
 #define	RELEASED	0
@@ -160,7 +160,8 @@ clkintr(struct trapframe *frame)
 		clkintr_pending = 0;
 		mtx_unlock_spin(&clock_lock);
 	}
-	KASSERT(!using_lapic_timer, ("clk interrupt enabled with lapic timer"));
+	KASSERT(using_lapic_timer == LAPIC_CLOCK_NONE,
+	    ("clk interrupt enabled with lapic timer"));
 
 	if (using_atrtc_timer) {
 #ifdef SMP
@@ -422,7 +423,7 @@ set_i8254_freq(u_int freq, int intr_freq
 	i8254_timecounter.tc_frequency = freq;
 	mtx_lock_spin(&clock_lock);
 	i8254_freq = freq;
-	if (using_lapic_timer)
+	if (using_lapic_timer != LAPIC_CLOCK_NONE)
 		new_i8254_real_max_count = 0x10000;
 	else
 		new_i8254_real_max_count = TIMER_DIV(intr_freq);
@@ -485,7 +486,7 @@ cpu_initclocks()
 	 * that it can drive hardclock().  Otherwise, change the 8254
 	 * timecounter to user a simpler algorithm.
 	 */
-	if (!using_lapic_timer) {
+	if (using_lapic_timer == LAPIC_CLOCK_NONE) {
 		intr_add_handler("clk", 0, (driver_filter_t *)clkintr, NULL,
 		    NULL, INTR_TYPE_CLK, NULL);
 		i8254_intsrc = intr_lookup_source(0);
@@ -508,7 +509,7 @@ cpu_initclocks()
 	 * kernel clocks, then setup the RTC to periodically interrupt to
 	 * drive statclock() and profclock().
 	 */
-	if (!using_lapic_timer) {
+	if (using_lapic_timer != LAPIC_CLOCK_ALL) {
 		using_atrtc_timer = atrtc_setup_clock();
 		if (using_atrtc_timer) {
 			/* Enable periodic interrupts from the RTC. */
@@ -532,7 +533,7 @@ void
 cpu_startprofclock(void)
 {
 
-	if (using_lapic_timer || !using_atrtc_timer)
+	if (using_lapic_timer == LAPIC_CLOCK_ALL || !using_atrtc_timer)
 		return;
 	atrtc_rate(RTCSA_PROF);
 	psdiv = pscnt = psratio;
@@ -542,7 +543,7 @@ void
 cpu_stopprofclock(void)
 {
 
-	if (using_lapic_timer || !using_atrtc_timer)
+	if (using_lapic_timer == LAPIC_CLOCK_ALL || !using_atrtc_timer)
 		return;
 	atrtc_rate(RTCSA_NOPROF);
 	psdiv = pscnt = 1;

Modified: head/sys/conf/options.i386
==============================================================================
--- head/sys/conf/options.i386	Fri Jan 15 16:01:22 2010	(r202386)
+++ head/sys/conf/options.i386	Fri Jan 15 16:04:30 2010	(r202387)
@@ -105,6 +105,7 @@ NETGRAPH_CRONYX		opt_ng_cronyx.h
 
 # Device options
 DEV_APIC		opt_apic.h
+DEV_ATPIC		opt_atpic.h
 DEV_NPX			opt_npx.h
 ASR_COMPAT		opt_asr.h
 

Modified: head/sys/i386/i386/local_apic.c
==============================================================================
--- head/sys/i386/i386/local_apic.c	Fri Jan 15 16:01:22 2010	(r202386)
+++ head/sys/i386/i386/local_apic.c	Fri Jan 15 16:04:30 2010	(r202387)
@@ -34,6 +34,7 @@
 #include <sys/cdefs.h>
 __FBSDID("$FreeBSD$");
 
+#include "opt_atpic.h"
 #include "opt_hwpmc_hooks.h"
 #include "opt_kdtrace.h"
 
@@ -160,6 +161,17 @@ static uint32_t	lvt_mode(struct lapic *l
 
 struct pic lapic_pic = { .pic_resume = lapic_resume };
 
+/*
+ * The atrtc device is compiled in only if atpic is present.
+ * If it is not, force lapic to take care of all the clocks.
+ */
+#ifdef DEV_ATPIC
+static int lapic_allclocks;
+TUNABLE_INT("machdep.lapic_allclocks", &lapic_allclocks);
+#else
+static int lapic_allclocks = 1;
+#endif
+
 static uint32_t
 lvt_mode(struct lapic *la, u_int pin, uint32_t value)
 {
@@ -416,11 +428,9 @@ lapic_disable_pmc(void)
 
 /*
  * Called by cpu_initclocks() on the BSP to setup the local APIC timer so
- * that it can drive hardclock, statclock, and profclock.  This function
- * returns true if it is able to use the local APIC timer to drive the
- * clocks and false if it is not able.
+ * that it can drive hardclock, statclock, and profclock. 
  */
-int
+enum lapic_clock
 lapic_setup_clock(void)
 {
 	u_long value;
@@ -428,10 +438,10 @@ lapic_setup_clock(void)
 
 	/* Can't drive the timer without a local APIC. */
 	if (lapic == NULL)
-		return (0);
+		return (LAPIC_CLOCK_NONE);
 
 	if (resource_int_value("apic", 0, "clock", &i) == 0 && i == 0)
-		return (0);
+		return (LAPIC_CLOCK_NONE);
 
 	/* Start off with a divisor of 2 (power on reset default). */
 	lapic_timer_divisor = 2;
@@ -463,19 +473,27 @@ lapic_setup_clock(void)
 	 * (and profhz) run at hz.  If 'hz' is below 1500 but above
 	 * 750, then we let the lapic timer run at 2 * 'hz'.  If 'hz'
 	 * is below 750 then we let the lapic timer run at 4 * 'hz'.
+	 *
+	 * Please note that stathz and profhz are set only if all the
+	 * clocks are handled through the local APIC.
 	 */
-	if (hz >= 1500)
+	if (lapic_allclocks != 0) {
+		if (hz >= 1500)
+			lapic_timer_hz = hz;
+		else if (hz >= 750)
+			lapic_timer_hz = hz * 2;
+		else
+			lapic_timer_hz = hz * 4;
+	} else
 		lapic_timer_hz = hz;
-	else if (hz >= 750)
-		lapic_timer_hz = hz * 2;
-	else
-		lapic_timer_hz = hz * 4;
-	if (lapic_timer_hz < 128)
-		stathz = lapic_timer_hz;
-	else
-		stathz = lapic_timer_hz / (lapic_timer_hz / 128);
-	profhz = lapic_timer_hz;
 	lapic_timer_period = value / lapic_timer_hz;
+	if (lapic_allclocks != 0) {
+		if (lapic_timer_hz < 128)
+			stathz = lapic_timer_hz;
+		else
+			stathz = lapic_timer_hz / (lapic_timer_hz / 128);
+		profhz = lapic_timer_hz;
+	}
 
 	/*
 	 * Start up the timer on the BSP.  The APs will kick off their
@@ -483,7 +501,7 @@ lapic_setup_clock(void)
 	 */
 	lapic_timer_periodic(lapic_timer_period);
 	lapic_timer_enable_intr();
-	return (1);
+	return (lapic_allclocks == 0 ? LAPIC_CLOCK_HARDCLOCK : LAPIC_CLOCK_ALL);
 }
 
 void
@@ -786,20 +804,23 @@ lapic_handle_timer(struct trapframe *fra
 		else
 			hardclock_cpu(TRAPF_USERMODE(frame));
 	}
+	if (lapic_allclocks != 0) {
 
-	/* Fire statclock at stathz. */
-	la->la_stat_ticks += stathz;
-	if (la->la_stat_ticks >= lapic_timer_hz) {
-		la->la_stat_ticks -= lapic_timer_hz;
-		statclock(TRAPF_USERMODE(frame));
-	}
+		/* Fire statclock at stathz. */
+		la->la_stat_ticks += stathz;
+		if (la->la_stat_ticks >= lapic_timer_hz) {
+			la->la_stat_ticks -= lapic_timer_hz;
+			statclock(TRAPF_USERMODE(frame));
+		}
 
-	/* Fire profclock at profhz, but only when needed. */
-	la->la_prof_ticks += profhz;
-	if (la->la_prof_ticks >= lapic_timer_hz) {
-		la->la_prof_ticks -= lapic_timer_hz;
-		if (profprocs != 0)
-			profclock(TRAPF_USERMODE(frame), TRAPF_PC(frame));
+		/* Fire profclock at profhz, but only when needed. */
+		la->la_prof_ticks += profhz;
+		if (la->la_prof_ticks >= lapic_timer_hz) {
+			la->la_prof_ticks -= lapic_timer_hz;
+			if (profprocs != 0)
+				profclock(TRAPF_USERMODE(frame),
+				    TRAPF_PC(frame));
+		}
 	}
 	critical_exit();
 }

Modified: head/sys/i386/include/apicvar.h
==============================================================================
--- head/sys/i386/include/apicvar.h	Fri Jan 15 16:01:22 2010	(r202386)
+++ head/sys/i386/include/apicvar.h	Fri Jan 15 16:04:30 2010	(r202387)
@@ -186,6 +186,12 @@
 #define	APIC_BUS_PCI		2
 #define	APIC_BUS_MAX		APIC_BUS_PCI
 
+enum lapic_clock {
+	LAPIC_CLOCK_NONE,
+	LAPIC_CLOCK_HARDCLOCK,
+	LAPIC_CLOCK_ALL
+};
+
 /*
  * An APIC enumerator is a psuedo bus driver that enumerates APIC's including
  * CPU's and I/O APIC's.
@@ -253,7 +259,7 @@ int	lapic_set_lvt_triggermode(u_int apic
 	    enum intr_trigger trigger);
 void	lapic_set_tpr(u_int vector);
 void	lapic_setup(int boot);
-int	lapic_setup_clock(void);
+enum lapic_clock	lapic_setup_clock(void);
 
 #endif /* !LOCORE */
 #endif /* _MACHINE_APICVAR_H_ */

Modified: head/sys/i386/isa/clock.c
==============================================================================
--- head/sys/i386/isa/clock.c	Fri Jan 15 16:01:22 2010	(r202386)
+++ head/sys/i386/isa/clock.c	Fri Jan 15 16:04:30 2010	(r202387)
@@ -106,7 +106,7 @@ static	u_int32_t i8254_offset;
 static	int	(*i8254_pending)(struct intsrc *);
 static	int	i8254_ticked;
 static	int	using_atrtc_timer;
-static	int	using_lapic_timer;
+static	enum lapic_clock using_lapic_timer = LAPIC_CLOCK_NONE;
 
 /* Values for timerX_state: */
 #define	RELEASED	0
@@ -175,7 +175,8 @@ clkintr(struct trapframe *frame)
 		clkintr_pending = 0;
 		mtx_unlock_spin(&clock_lock);
 	}
-	KASSERT(!using_lapic_timer, ("clk interrupt enabled with lapic timer"));
+	KASSERT(using_lapic_timer == LAPIC_CLOCK_NONE,
+	    ("clk interrupt enabled with lapic timer"));
 
 #ifdef KDTRACE_HOOKS
 	/*
@@ -453,7 +454,7 @@ set_i8254_freq(u_int freq, int intr_freq
 	i8254_timecounter.tc_frequency = freq;
 	mtx_lock_spin(&clock_lock);
 	i8254_freq = freq;
-	if (using_lapic_timer)
+	if (using_lapic_timer != LAPIC_CLOCK_NONE)
 		new_i8254_real_max_count = 0x10000;
 	else
 		new_i8254_real_max_count = TIMER_DIV(intr_freq);
@@ -533,7 +534,7 @@ cpu_initclocks()
 	 * that it can drive hardclock().  Otherwise, change the 8254
 	 * timecounter to user a simpler algorithm.
 	 */
-	if (!using_lapic_timer) {
+	if (using_lapic_timer == LAPIC_CLOCK_NONE) {
 		intr_add_handler("clk", 0, (driver_filter_t *)clkintr, NULL,
 		    NULL, INTR_TYPE_CLK, NULL);
 		i8254_intsrc = intr_lookup_source(0);
@@ -556,7 +557,7 @@ cpu_initclocks()
 	 * kernel clocks, then setup the RTC to periodically interrupt to
 	 * drive statclock() and profclock().
 	 */
-	if (!using_lapic_timer) {
+	if (using_lapic_timer != LAPIC_CLOCK_ALL) {
 		using_atrtc_timer = atrtc_setup_clock();
 		if (using_atrtc_timer) {
 			/* Enable periodic interrupts from the RTC. */
@@ -580,7 +581,7 @@ void
 cpu_startprofclock(void)
 {
 
-	if (using_lapic_timer || !using_atrtc_timer)
+	if (using_lapic_timer == LAPIC_CLOCK_ALL || !using_atrtc_timer)
 		return;
 	atrtc_rate(RTCSA_PROF);
 	psdiv = pscnt = psratio;
@@ -590,7 +591,7 @@ void
 cpu_stopprofclock(void)
 {
 
-	if (using_lapic_timer || !using_atrtc_timer)
+	if (using_lapic_timer == LAPIC_CLOCK_ALL || !using_atrtc_timer)
 		return;
 	atrtc_rate(RTCSA_NOPROF);
 	psdiv = pscnt = 1;

Modified: head/sys/pc98/cbus/clock.c
==============================================================================
--- head/sys/pc98/cbus/clock.c	Fri Jan 15 16:01:22 2010	(r202386)
+++ head/sys/pc98/cbus/clock.c	Fri Jan 15 16:04:30 2010	(r202387)
@@ -101,7 +101,7 @@ static	u_int32_t i8254_lastcount;
 static	u_int32_t i8254_offset;
 static	int	(*i8254_pending)(struct intsrc *);
 static	int	i8254_ticked;
-static	int	using_lapic_timer;
+static	enum lapic_clock using_lapic_timer = LAPIC_CLOCK_NONE;
 
 /* Values for timerX_state: */
 #define	RELEASED	0
@@ -164,7 +164,8 @@ clkintr(struct trapframe *frame)
 		clkintr_pending = 0;
 		mtx_unlock_spin(&clock_lock);
 	}
-	KASSERT(!using_lapic_timer, ("clk interrupt enabled with lapic timer"));
+	KASSERT(using_lapic_timer == LAPIC_CLOCK_NONE,
+	    ("clk interrupt enabled with lapic timer"));
 
 #ifdef KDTRACE_HOOKS
 	/*
@@ -360,7 +361,7 @@ set_i8254_freq(u_int freq, int intr_freq
 	i8254_timecounter.tc_frequency = freq;
 	mtx_lock_spin(&clock_lock);
 	i8254_freq = freq;
-	if (using_lapic_timer)
+	if (using_lapic_timer != LAPIC_CLOCK_NONE)
 		new_i8254_real_max_count = 0x10000;
 	else
 		new_i8254_real_max_count = TIMER_DIV(intr_freq);
@@ -443,7 +444,7 @@ cpu_initclocks()
 	 * that it can drive hardclock().  Otherwise, change the 8254
 	 * timecounter to user a simpler algorithm.
 	 */
-	if (!using_lapic_timer) {
+	if (using_lapic_timer == LAPIC_CLOCK_NONE) {
 		intr_add_handler("clk", 0, (driver_filter_t *)clkintr, NULL,
 		    NULL, INTR_TYPE_CLK, NULL);
 		i8254_intsrc = intr_lookup_source(0);


More information about the svn-src-head mailing list