mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 02:01:24 +01:00
x86: Clean up APIC local timer handling.
1. Writing TMICT=0 disables the timer. Use this fact to simplify and improve reprogram_timer(). In particular, we always write TMICT, and write zero when we do not need a timer interrupt. 2. In HPET broadcast timer handler, set TMICT=0 when we mask the APIC local timer. May as well do this early, before entering deep sleep. 3. In HVM-guest APIC emulation, disable the emulated local timer when the guest sets TMICT=0. Previously we would issue an immediate one-shot interrupt. Signed-off-by: Keir Fraser <keir.fraser@citrix.com>
This commit is contained in:
1 parent
ad1f74192a
commit
b95beb1858
3 files changed
+21
-55
No files matched your search
+9
-49
@@ -1178,65 +1178,25 @@ void enable_APIC_timer(void)
|
||||
#undef APIC_DIVISOR
|
||||
|
||||
/*
|
||||
* reprogram the APIC timer. Timeoutvalue is in ns from start of boot
|
||||
* returns 1 on success
|
||||
* returns 0 if the timeout value is too small or in the past.
|
||||
* reprogram_timer: Reprogram the APIC timer.
|
||||
* Timeout is a Xen system time (nanoseconds since boot); 0 disables the timer.
|
||||
* Returns 1 on success; 0 if the timeout is too soon or is in the past.
|
||||
*/
|
||||
int reprogram_timer(s_time_t timeout)
|
||||
{
|
||||
s_time_t now;
|
||||
s_time_t expire;
|
||||
u64 apic_tmict;
|
||||
s_time_t expire;
|
||||
u32 apic_tmict = 0;
|
||||
|
||||
/*
|
||||
* If we don't have local APIC then we just poll the timer list off the
|
||||
* PIT interrupt.
|
||||
*/
|
||||
/* No local APIC: timer list is polled via the PIT interrupt. */
|
||||
if ( !cpu_has_apic )
|
||||
return 1;
|
||||
|
||||
/*
|
||||
* We use this value because we don't trust zero (we think it may just
|
||||
* cause an immediate interrupt). At least this is guaranteed to hold it
|
||||
* off for ages (esp. since the clock ticks on bus clock, not cpu clock!).
|
||||
*/
|
||||
if ( timeout == 0 )
|
||||
{
|
||||
apic_tmict = 0xffffffff;
|
||||
goto reprogram;
|
||||
}
|
||||
if ( timeout && ((expire = timeout - NOW()) > 0) )
|
||||
apic_tmict = min_t(u64, (bus_scale * expire) >> 18, UINT_MAX);
|
||||
|
||||
now = NOW();
|
||||
expire = timeout - now; /* value from now */
|
||||
|
||||
if ( expire <= 0 )
|
||||
{
|
||||
Dprintk("APICT[%02d] Timeout in the past 0x%08X%08X > 0x%08X%08X\n",
|
||||
smp_processor_id(), (u32)(now>>32),
|
||||
(u32)now, (u32)(timeout>>32),(u32)timeout);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* conversion to bus units */
|
||||
apic_tmict = (((u64)bus_scale) * expire)>>18;
|
||||
|
||||
if ( apic_tmict >= 0xffffffff )
|
||||
{
|
||||
Dprintk("APICT[%02d] Timeout value too large\n", smp_processor_id());
|
||||
apic_tmict = 0xffffffff;
|
||||
}
|
||||
|
||||
if ( apic_tmict == 0 )
|
||||
{
|
||||
Dprintk("APICT[%02d] timeout value too small\n", smp_processor_id());
|
||||
return 0;
|
||||
}
|
||||
|
||||
reprogram:
|
||||
/* Program the timer. */
|
||||
apic_write(APIC_TMICT, (unsigned long)apic_tmict);
|
||||
|
||||
return 1;
|
||||
return apic_tmict || !timeout;
|
||||
}
|
||||
|
||||
fastcall void smp_apic_timer_interrupt(struct cpu_user_regs * regs)
|
||||
|
||||
+3
-4
@@ -642,6 +642,8 @@ void hpet_broadcast_enter(void)
|
||||
if ( hpet_attach_channel )
|
||||
hpet_attach_channel(cpu, ch);
|
||||
|
||||
/* Cancel any outstanding LAPIC timer event and disable interrupts. */
|
||||
reprogram_timer(0);
|
||||
disable_APIC_timer();
|
||||
|
||||
cpu_set(cpu, ch->cpumask);
|
||||
@@ -664,11 +666,8 @@ void hpet_broadcast_exit(void)
|
||||
|
||||
if ( cpu_test_and_clear(cpu, ch->cpumask) )
|
||||
{
|
||||
/* Cancel any outstanding LAPIC event and re-enable interrupts. */
|
||||
reprogram_timer(0);
|
||||
enable_APIC_timer();
|
||||
|
||||
/* Reprogram the deadline; trigger timer work now if it has passed. */
|
||||
enable_APIC_timer();
|
||||
if ( !reprogram_timer(per_cpu(timer_deadline, cpu)) )
|
||||
raise_softirq(TIMER_SOFTIRQ);
|
||||
|
||||
|
||||
@@ -667,10 +667,17 @@ static int vlapic_write(struct vcpu *v, unsigned long address,
|
||||
|
||||
case APIC_TMICT:
|
||||
{
|
||||
uint64_t period = (uint64_t)APIC_BUS_CYCLE_NS *
|
||||
(uint32_t)val * vlapic->hw.timer_divisor;
|
||||
uint64_t period;
|
||||
|
||||
vlapic_set_reg(vlapic, APIC_TMICT, val);
|
||||
if ( val == 0 )
|
||||
{
|
||||
destroy_periodic_time(&vlapic->pt);
|
||||
break;
|
||||
}
|
||||
|
||||
period = ((uint64_t)APIC_BUS_CYCLE_NS *
|
||||
(uint32_t)val * vlapic->hw.timer_divisor);
|
||||
create_periodic_time(current, &vlapic->pt, period,
|
||||
vlapic_lvtt_period(vlapic) ? period : 0,
|
||||
vlapic->pt.irq, vlapic_pt_cb,
|
||||
|
||||