Some changes around LAPIC timer programming.
This fixes heavy interrupt storm and resulting system freeze when using LAPIC timer in one-shot mode under Xen HVM. There, unlike real hardware, programming timer with zero period almost immediately causes interrupt.
This commit is contained in:
parent
7798640d82
commit
00aa5aab1e
@ -159,9 +159,8 @@ static struct eventtimer lapic_et;
|
||||
|
||||
static void lapic_enable(void);
|
||||
static void lapic_resume(struct pic *pic);
|
||||
static void lapic_timer_enable_intr(void);
|
||||
static void lapic_timer_oneshot(u_int count);
|
||||
static void lapic_timer_periodic(u_int count);
|
||||
static void lapic_timer_oneshot(u_int count, int enable_int);
|
||||
static void lapic_timer_periodic(u_int count, int enable_int);
|
||||
static void lapic_timer_stop(void);
|
||||
static void lapic_timer_set_divisor(u_int divisor);
|
||||
static uint32_t lvt_mode(struct lapic *la, u_int pin, uint32_t value);
|
||||
@ -379,13 +378,11 @@ lapic_setup(int boot)
|
||||
if (la->la_timer_mode != 0) {
|
||||
KASSERT(la->la_timer_period != 0, ("lapic%u: zero divisor",
|
||||
lapic_id()));
|
||||
lapic_timer_stop();
|
||||
lapic_timer_set_divisor(lapic_timer_divisor);
|
||||
lapic_timer_enable_intr();
|
||||
if (la->la_timer_mode == 1)
|
||||
lapic_timer_periodic(la->la_timer_period);
|
||||
lapic_timer_periodic(la->la_timer_period, 1);
|
||||
else
|
||||
lapic_timer_oneshot(la->la_timer_period);
|
||||
lapic_timer_oneshot(la->la_timer_period, 1);
|
||||
}
|
||||
|
||||
/* Program error LVT and clear any existing errors. */
|
||||
@ -496,7 +493,7 @@ lapic_et_start(struct eventtimer *et,
|
||||
/* Try to calibrate the local APIC timer. */
|
||||
do {
|
||||
lapic_timer_set_divisor(lapic_timer_divisor);
|
||||
lapic_timer_oneshot(APIC_TIMER_MAX_COUNT);
|
||||
lapic_timer_oneshot(APIC_TIMER_MAX_COUNT, 0);
|
||||
DELAY(1000000);
|
||||
value = APIC_TIMER_MAX_COUNT - lapic->ccr_timer;
|
||||
if (value != APIC_TIMER_MAX_COUNT)
|
||||
@ -516,9 +513,7 @@ lapic_et_start(struct eventtimer *et,
|
||||
et->et_max_period.frac =
|
||||
((0xfffffffeLLU << 32) / et->et_frequency) << 32;
|
||||
}
|
||||
lapic_timer_stop();
|
||||
lapic_timer_set_divisor(lapic_timer_divisor);
|
||||
lapic_timer_enable_intr();
|
||||
la = &lapics[lapic_id()];
|
||||
if (period != NULL) {
|
||||
la->la_timer_mode = 1;
|
||||
@ -526,14 +521,14 @@ lapic_et_start(struct eventtimer *et,
|
||||
(et->et_frequency * (period->frac >> 32)) >> 32;
|
||||
if (period->sec != 0)
|
||||
la->la_timer_period += et->et_frequency * period->sec;
|
||||
lapic_timer_periodic(la->la_timer_period);
|
||||
lapic_timer_periodic(la->la_timer_period, 1);
|
||||
} else {
|
||||
la->la_timer_mode = 2;
|
||||
la->la_timer_period =
|
||||
(et->et_frequency * (first->frac >> 32)) >> 32;
|
||||
if (first->sec != 0)
|
||||
la->la_timer_period += et->et_frequency * first->sec;
|
||||
lapic_timer_oneshot(la->la_timer_period);
|
||||
lapic_timer_oneshot(la->la_timer_period, 1);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
@ -838,25 +833,29 @@ lapic_timer_set_divisor(u_int divisor)
|
||||
}
|
||||
|
||||
static void
|
||||
lapic_timer_oneshot(u_int count)
|
||||
lapic_timer_oneshot(u_int count, int enable_int)
|
||||
{
|
||||
u_int32_t value;
|
||||
|
||||
value = lapic->lvt_timer;
|
||||
value &= ~APIC_LVTT_TM;
|
||||
value |= APIC_LVTT_TM_ONE_SHOT;
|
||||
if (enable_int)
|
||||
value &= ~APIC_LVT_M;
|
||||
lapic->lvt_timer = value;
|
||||
lapic->icr_timer = count;
|
||||
}
|
||||
|
||||
static void
|
||||
lapic_timer_periodic(u_int count)
|
||||
lapic_timer_periodic(u_int count, int enable_int)
|
||||
{
|
||||
u_int32_t value;
|
||||
|
||||
value = lapic->lvt_timer;
|
||||
value &= ~APIC_LVTT_TM;
|
||||
value |= APIC_LVTT_TM_PERIODIC;
|
||||
if (enable_int)
|
||||
value &= ~APIC_LVT_M;
|
||||
lapic->lvt_timer = value;
|
||||
lapic->icr_timer = count;
|
||||
}
|
||||
@ -870,17 +869,6 @@ lapic_timer_stop(void)
|
||||
value &= ~APIC_LVTT_TM;
|
||||
value |= APIC_LVT_M;
|
||||
lapic->lvt_timer = value;
|
||||
lapic->icr_timer = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
lapic_timer_enable_intr(void)
|
||||
{
|
||||
u_int32_t value;
|
||||
|
||||
value = lapic->lvt_timer;
|
||||
value &= ~APIC_LVT_M;
|
||||
lapic->lvt_timer = value;
|
||||
}
|
||||
|
||||
void
|
||||
|
Loading…
x
Reference in New Issue
Block a user