diff --git a/xen/arch/x86/irq.c b/xen/arch/x86/irq.c index d6451a903b..335c8ffb67 100644 --- a/xen/arch/x86/irq.c +++ b/xen/arch/x86/irq.c @@ -1117,6 +1117,13 @@ static void irq_guest_eoi_timer_fn(void *data) action = (irq_guest_action_t *)desc->action; + /* + * Is another instance of this timer already running? Skip everything + * to avoid forcing an EOI early. + */ + if ( timer_is_active(&action->eoi_timer) ) + goto out; + if ( action->ack_type != ACKTYPE_NONE ) { unsigned int i; @@ -1168,6 +1175,13 @@ static void __do_IRQ_guest(int irq) return; } + /* + * Stop the timer as soon as we're certain we'll set it again further down, + * to prevent the current timeout (if any) to needlessly expire. + */ + if ( action->ack_type != ACKTYPE_NONE ) + stop_timer(&action->eoi_timer); + if ( action->ack_type == ACKTYPE_EOI ) { sp = pending_eoi_sp(peoi); @@ -1195,7 +1209,6 @@ static void __do_IRQ_guest(int irq) if ( action->ack_type != ACKTYPE_NONE ) { - stop_timer(&action->eoi_timer); migrate_timer(&action->eoi_timer, smp_processor_id()); set_timer(&action->eoi_timer, NOW() + MILLISECS(1)); } @@ -1458,6 +1471,8 @@ void desc_guest_eoi(struct irq_desc *desc, struct pirq *pirq) return; } + stop_timer(&action->eoi_timer); + if ( action->ack_type == ACKTYPE_UNMASK ) { ASSERT(cpumask_empty(action->cpu_eoi_map)); diff --git a/xen/common/timer.c b/xen/common/timer.c index c85273bf82..80531d855d 100644 --- a/xen/common/timer.c +++ b/xen/common/timer.c @@ -282,11 +282,10 @@ static inline void timer_unlock(struct timer *timer) }) -static bool_t active_timer(struct timer *timer) +static bool active_timer(const struct timer *timer) { ASSERT(timer->status >= TIMER_STATUS_inactive); - ASSERT(timer->status <= TIMER_STATUS_in_list); - return (timer->status >= TIMER_STATUS_in_heap); + return timer_is_active(timer); } diff --git a/xen/include/xen/timer.h b/xen/include/xen/timer.h index 4513260b0d..3a2a05c6de 100644 --- a/xen/include/xen/timer.h +++ b/xen/include/xen/timer.h @@ -75,6 +75,19 @@ bool timer_expires_before(struct timer *timer, s_time_t t); #define timer_is_expired(t) timer_expires_before(t, NOW()) +/* + * True if a timer is active. + * + * Unlike for timer_expires_before(), it is the caller's responsibility to + * use suitable locking such that the returned value isn't stale by the time + * it gets acted upon. + */ +static inline bool timer_is_active(const struct timer *timer) +{ + ASSERT(timer->status <= TIMER_STATUS_in_list); + return timer->status >= TIMER_STATUS_in_heap; +} + /* Migrate a timer to a different CPU. The timer may be currently active. */ void migrate_timer(struct timer *timer, unsigned int new_cpu);