Timekeeping hosed by factor 3, high lapic[01] interrupt rates
John Baldwin
jhb at FreeBSD.org
Fri Jun 24 19:28:50 GMT 2005
On Friday 24 June 2005 01:53 pm, John Baldwin wrote:
> On Friday 24 June 2005 12:50 pm, Jens Schweikhardt wrote:
> > On Thu, Jun 23, 2005 at 05:14:39PM -0400, John Baldwin wrote:
> > ...
> > # Ok. What timecounter does your UP kernel use, and does your UP kernel
> > break # if you change the timecounter to i8254?
> >
> > The UP uses the TSC:
> > $ sysctl -a|grep timec
> > kern.timecounter.stepwarnings: 0
> > kern.timecounter.nbinuptime: 136311
> > kern.timecounter.nnanouptime: 0
> > kern.timecounter.nmicrouptime: 664
> > kern.timecounter.nbintime: 1273
> > kern.timecounter.nnanotime: 36
> > kern.timecounter.nmicrotime: 1237
> > kern.timecounter.ngetbinuptime: 405
> > kern.timecounter.ngetnanouptime: 29
> > kern.timecounter.ngetmicrouptime: 2534
> > kern.timecounter.ngetbintime: 0
> > kern.timecounter.ngetnanotime: 0
> > kern.timecounter.ngetmicrotime: 5
> > kern.timecounter.nsetclock: 2
> > kern.timecounter.hardware: TSC
> > kern.timecounter.choice: TSC(800) i8254(0) dummy(-1000000)
> > kern.timecounter.tick: 1
> > kern.timecounter.smp_tsc: 0
> >
> > When I do
> >
> > $ sysctl kern.timecounter.hardware=i8254
> >
> > on the UP the time dilation by factor 3 starts and the lapic rate
> > increases. So yes, that breaks the UP kernel.
>
> Ok. Can you try this untested patch? It's mostly from phk and I haven't
> yet tested it locally to make sure it doesn't break things:
Scratch that. I've reproduced this locally now on a testbox I have and had to
add a bugfix from phk to get it to work. Here's the patch that works for me:
--- //depot/vendor/freebsd/src/sys/i386/isa/clock.c 2005/05/14 09:10:26
+++ //depot/user/jhb/acpipci/i386/isa/clock.c 2005/06/24 19:19:38
@@ -136,6 +136,7 @@
static u_char timer2_state;
static unsigned i8254_get_timecount(struct timecounter *tc);
+static unsigned i8254_simple_get_timecount(struct timecounter *tc);
static void set_timer_freq(u_int freq, int intr_freq);
static struct timecounter i8254_timecounter = {
@@ -532,10 +533,15 @@
{
int new_timer0_max_count;
+ i8254_timecounter.tc_frequency = freq;
mtx_lock_spin(&clock_lock);
timer_freq = freq;
new_timer0_max_count = hardclock_max_count = TIMER_DIV(intr_freq);
- if (new_timer0_max_count != timer0_max_count) {
+ if (using_lapic_timer) {
+ outb(TIMER_MODE, TIMER_SEL0 | TIMER_RATEGEN | TIMER_16BIT);
+ outb(TIMER_CNTR0, 0);
+ outb(TIMER_CNTR0, 0);
+ } else if (new_timer0_max_count != timer0_max_count) {
timer0_max_count = new_timer0_max_count;
outb(TIMER_MODE, TIMER_SEL0 | TIMER_RATEGEN | TIMER_16BIT);
outb(TIMER_CNTR0, timer0_max_count & 0xff);
@@ -548,11 +554,7 @@
i8254_restore(void)
{
- mtx_lock_spin(&clock_lock);
- outb(TIMER_MODE, TIMER_SEL0 | TIMER_RATEGEN | TIMER_16BIT);
- outb(TIMER_CNTR0, timer0_max_count & 0xff);
- outb(TIMER_CNTR0, timer0_max_count >> 8);
- mtx_unlock_spin(&clock_lock);
+ set_timer_freq(timer_freq, hz);
}
static void
@@ -627,7 +629,6 @@
}
set_timer_freq(timer_freq, hz);
- i8254_timecounter.tc_frequency = timer_freq;
tc_init(&i8254_timecounter);
init_TSC();
@@ -782,7 +783,8 @@
/*
* If we aren't using the local APIC timer to drive the kernel
* clocks, setup the interrupt handler for the 8254 timer 0 so
- * that it can drive hardclock().
+ * that it can drive hardclock(). Otherwise, change the 8254
+ * timecounter to user a simpler algorithm.
*/
if (!using_lapic_timer) {
intr_add_handler("clk", 0, (driver_intr_t *)clkintr, NULL,
@@ -791,6 +793,11 @@
if (i8254_intsrc != NULL)
i8254_pending =
i8254_intsrc->is_pic->pic_source_pending;
+ } else {
+ i8254_timecounter.tc_get_timecount =
+ i8254_simple_get_timecount;
+ i8254_timecounter.tc_counter_mask = 0xffff;
+ set_timer_freq(timer_freq, hz);
}
/* Initialize RTC. */
@@ -858,10 +865,8 @@
*/
freq = timer_freq;
error = sysctl_handle_int(oidp, &freq, sizeof(freq), req);
- if (error == 0 && req->newptr != NULL) {
+ if (error == 0 && req->newptr != NULL)
set_timer_freq(freq, hz);
- i8254_timecounter.tc_frequency = freq;
- }
return (error);
}
@@ -869,6 +874,24 @@
0, sizeof(u_int), sysctl_machdep_i8254_freq, "IU", "");
static unsigned
+i8254_simple_get_timecount(struct timecounter *tc)
+{
+ u_int count;
+ u_int high, low;
+
+ mtx_lock_spin(&clock_lock);
+
+ /* Select timer0 and latch counter value. */
+ outb(TIMER_MODE, TIMER_SEL0 | TIMER_LATCH);
+
+ low = inb(TIMER_CNTR0);
+ high = inb(TIMER_CNTR0);
+ count = 0xffff - ((high << 8) | low);
+ mtx_unlock_spin(&clock_lock);
+ return (count);
+}
+
+static unsigned
i8254_get_timecount(struct timecounter *tc)
{
u_int count;
--
John Baldwin <jhb at FreeBSD.org> <>< http://www.FreeBSD.org/~jhb/
"Power Users Use the Power to Serve" = http://www.FreeBSD.org
More information about the freebsd-current
mailing list