Index: head/sys/conf/options.i386 =================================================================== --- head/sys/conf/options.i386 (revision 199869) +++ head/sys/conf/options.i386 (working copy) @@ -105,6 +105,7 @@ # Device options DEV_APIC opt_apic.h +DEV_ATPIC opt_atpic.h DEV_NPX opt_npx.h ASR_COMPAT opt_asr.h Index: head/sys/pc98/cbus/clock.c =================================================================== --- head/sys/pc98/cbus/clock.c (revision 199869) +++ head/sys/pc98/cbus/clock.c (working copy) @@ -101,7 +101,7 @@ 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_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 @@ 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 @@ * 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); Index: head/sys/i386/include/apicvar.h =================================================================== --- head/sys/i386/include/apicvar.h (revision 199869) +++ head/sys/i386/include/apicvar.h (working copy) @@ -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 @@ 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_ */ Index: head/sys/i386/i386/local_apic.c =================================================================== --- head/sys/i386/i386/local_apic.c (revision 199869) +++ head/sys/i386/i386/local_apic.c (working copy) @@ -34,6 +34,7 @@ #include __FBSDID("$FreeBSD$"); +#include "opt_atpic.h" #include "opt_hwpmc_hooks.h" #include "opt_kdtrace.h" @@ -160,6 +161,17 @@ 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 @@ /* * 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 @@ /* 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,6 +473,9 @@ * (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) lapic_timer_hz = hz; @@ -470,12 +483,14 @@ 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 +498,7 @@ */ 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 +801,23 @@ 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(); } Index: head/sys/i386/isa/clock.c =================================================================== --- head/sys/i386/isa/clock.c (revision 199869) +++ head/sys/i386/isa/clock.c (working copy) @@ -106,7 +106,7 @@ 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_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 @@ 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 @@ * 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 @@ * 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 @@ 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 @@ 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; Index: head/sys/amd64/include/apicvar.h =================================================================== --- head/sys/amd64/include/apicvar.h (revision 199869) +++ head/sys/amd64/include/apicvar.h (working copy) @@ -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 @@ 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_ */ Index: head/sys/amd64/amd64/local_apic.c =================================================================== --- head/sys/amd64/amd64/local_apic.c (revision 199869) +++ head/sys/amd64/amd64/local_apic.c (working copy) @@ -160,6 +160,9 @@ 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 @@ /* * 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 @@ /* 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,6 +465,9 @@ * (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) lapic_timer_hz = hz; @@ -468,12 +475,14 @@ 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 +490,7 @@ */ 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 +793,23 @@ 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(); } Index: head/sys/amd64/isa/clock.c =================================================================== --- head/sys/amd64/isa/clock.c (revision 199869) +++ head/sys/amd64/isa/clock.c (working copy) @@ -91,7 +91,7 @@ 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_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 @@ 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 @@ * 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 @@ * 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 @@ 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 @@ 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;