x86/HVM: properly handle RTC periodic timer even when !RTC_PIE Since in that case the processing it pr_intr_post() won't occur, we need to do some additional work in pt_update_irq(). Additionally we must not pay attention to the respective IRQ being masked. Signed-off-by: Jan Beulich --- a/xen/arch/x86/hvm/rtc.c +++ b/xen/arch/x86/hvm/rtc.c @@ -67,11 +67,13 @@ static void rtc_update_irq(RTCState *s) hvm_isa_irq_assert(vrtc_domain(s), RTC_IRQ); } -void rtc_periodic_interrupt(void *opaque) +bool_t rtc_periodic_interrupt(void *opaque) { RTCState *s = opaque; + bool_t ret; spin_lock(&s->lock); + ret = !(s->hw.cmos_data[RTC_REG_C] & RTC_IRQF); if ( !(s->hw.cmos_data[RTC_REG_C] & RTC_PF) ) { s->hw.cmos_data[RTC_REG_C] |= RTC_PF; @@ -83,7 +85,11 @@ void rtc_periodic_interrupt(void *opaque destroy_periodic_time(&s->pt); s->pt_code = 0; } + if ( !(s->hw.cmos_data[RTC_REG_C] & RTC_IRQF) ) + ret = 0; spin_unlock(&s->lock); + + return ret; } /* Enable/configure/disable the periodic timer based on the RTC_PIE and --- a/xen/arch/x86/hvm/vpt.c +++ b/xen/arch/x86/hvm/vpt.c @@ -216,18 +216,23 @@ static void pt_timer_fn(void *data) int pt_update_irq(struct vcpu *v) { struct list_head *head = &v->arch.hvm_vcpu.tm_list; - struct periodic_time *pt, *temp, *earliest_pt = NULL; - uint64_t max_lag = -1ULL; + struct periodic_time *pt, *temp, *earliest_pt; + uint64_t max_lag; int irq, is_lapic; void *pt_priv; + rescan: spin_lock(&v->arch.hvm_vcpu.tm_lock); + rescan_locked: + earliest_pt = NULL; + max_lag = -1ULL; list_for_each_entry_safe ( pt, temp, head, list ) { if ( pt->pending_intr_nr ) { - if ( pt_irq_masked(pt) ) + /* RTC code takes care of disabling the timer itself. */ + if ( (pt->irq != RTC_IRQ || !pt->priv) && pt_irq_masked(pt) ) { /* suspend timer emulation */ list_del(&pt->list); @@ -260,7 +265,41 @@ int pt_update_irq(struct vcpu *v) if ( is_lapic ) vlapic_set_irq(vcpu_vlapic(v), irq, 0); else if ( irq == RTC_IRQ && pt_priv ) - rtc_periodic_interrupt(pt_priv); + { + if ( !rtc_periodic_interrupt(pt_priv) ) + irq = -1; + + pt_lock(earliest_pt); + + if ( irq < 0 && earliest_pt->pending_intr_nr ) + { + /* + * RTC periodic timer runs without the corresponding interrupt + * being enabled - need to mimic enough of pt_intr_post() to keep + * things going. + */ + earliest_pt->pending_intr_nr = 0; + earliest_pt->irq_issued = 0; + set_timer(&earliest_pt->timer, earliest_pt->scheduled); + } + else if ( irq >= 0 && pt_irq_masked(earliest_pt) ) + { + if ( earliest_pt->on_list ) + { + /* suspend timer emulation */ + list_del(&earliest_pt->list); + earliest_pt->on_list = 0; + } + irq = -1; + } + + /* Avoid dropping the lock if we can. */ + if ( irq < 0 && v == earliest_pt->vcpu ) + goto rescan_locked; + pt_unlock(earliest_pt); + if ( irq < 0 ) + goto rescan; + } else { hvm_isa_irq_deassert(v->domain, irq); --- a/xen/include/asm-x86/hvm/vpt.h +++ b/xen/include/asm-x86/hvm/vpt.h @@ -183,7 +183,7 @@ void rtc_migrate_timers(struct vcpu *v); void rtc_deinit(struct domain *d); void rtc_reset(struct domain *d); void rtc_update_clock(struct domain *d); -void rtc_periodic_interrupt(void *); +bool_t rtc_periodic_interrupt(void *); void pmtimer_init(struct vcpu *v); void pmtimer_deinit(struct domain *d);