Signed-off-by: Haitao Shan diff -r ec6d1ed854fb xen/arch/x86/hvm/vlapic.c --- a/xen/arch/x86/hvm/vlapic.c Thu Jul 03 10:47:27 2008 +0800 +++ b/xen/arch/x86/hvm/vlapic.c Fri Jul 04 15:13:53 2008 +0800 @@ -413,9 +413,8 @@ if ( vlapic_test_and_clear_vector(vector, &vlapic->regs->data[APIC_TMR]) ) vioapic_update_EOI(vlapic_domain(vlapic), vector); - - if ( iommu_enabled ) - hvm_dpci_msi_eoi(current->domain, vector); + + hvm_dpci_msi_eoi(current->domain, vector); } static int vlapic_ipi( diff -r ec6d1ed854fb xen/arch/x86/irq.c --- a/xen/arch/x86/irq.c Thu Jul 03 10:47:27 2008 +0800 +++ b/xen/arch/x86/irq.c Fri Jul 04 15:13:53 2008 +0800 @@ -22,6 +22,7 @@ boolean_param("noirqbalance", opt_noirqbalance); irq_desc_t irq_desc[NR_IRQS]; +struct timer irq_timer[NR_IRQS]; static void __do_IRQ_guest(int vector); @@ -48,6 +49,17 @@ end_none }; +static void irq_timer_fn(void *data) +{ + irq_desc_t *desc = data; + unsigned vector = desc - irq_desc; + unsigned long flags; + + spin_lock_irqsave(&desc->lock, flags); + desc->handler->enable(vector); + spin_unlock_irqrestore(&desc->lock, flags); +} + atomic_t irq_err_count; asmlinkage void do_IRQ(struct cpu_user_regs *regs) @@ -59,6 +71,22 @@ perfc_incr(irqs); spin_lock(&desc->lock); + + /* For ACK-TYPE_NONE IRQs, we mask the second interrupt if the first + * interrupt is still in service. A timer will be set so that 1ms later + * this IRQ can be enabled again. + */ + if ( unlikely(desc->status & IRQ_INPROGRESS) ) + { + desc->handler->disable(vector); + desc->handler->ack(vector); + stop_timer(&irq_timer[vector]); + init_timer(&irq_timer[vector], irq_timer_fn, desc, smp_processor_id()); + set_timer(&irq_timer[vector], NOW() + MILLISECS(1)); + spin_unlock(&desc->lock); + return; + } + desc->handler->ack(vector); if ( likely(desc->status & IRQ_GUEST) ) @@ -206,8 +234,11 @@ irq_desc_t *desc = &irq_desc[vector]; irq_guest_action_t *action = (irq_guest_action_t *)desc->action; struct domain *d; - int i, sp; + int i, sp, is_hvm_edge_irq = 0, is_edge_pirq_pending = 0; struct pending_eoi *peoi = this_cpu(pending_eoi); + + if ( action->ack_type == ACKTYPE_NONE ) + desc->status |= IRQ_INPROGRESS; if ( unlikely(action->nr_guests == 0) ) { @@ -238,9 +269,25 @@ !test_and_set_bit(irq, d->pirq_mask) ) action->in_flight++; if (!hvm_do_IRQ_dpci(d, irq)) - send_guest_pirq(d, irq); + is_edge_pirq_pending |= send_guest_pirq(d, irq) && + (action->ack_type == ACKTYPE_NONE); + else + is_hvm_edge_irq = action->ack_type == ACKTYPE_NONE; + } + /* For ACK-TYPE-NONE IRQs delivered to PV guests, we can not identity the + * second interrupt until we reach this point. So we mask this IRQ here. + */ + if ( unlikely(is_edge_pirq_pending) ) + { + desc->handler->disable(vector); + stop_timer(&irq_timer[vector]); + init_timer(&irq_timer[vector], irq_timer_fn, desc, smp_processor_id()); + set_timer(&irq_timer[vector], NOW() + MILLISECS(1)); } + + if ( !is_hvm_edge_irq ) + desc->status &= ~IRQ_INPROGRESS; } /* Flush all ready EOIs from the top of this CPU's pending-EOI stack. */ @@ -622,6 +669,8 @@ desc->action = NULL; xfree(action); desc->status &= ~IRQ_GUEST; + desc->status &= ~IRQ_INPROGRESS; + kill_timer(&irq_timer[vector]); desc->handler->shutdown(vector); out: diff -r ec6d1ed854fb xen/common/event_channel.c --- a/xen/common/event_channel.c Thu Jul 03 10:47:27 2008 +0800 +++ b/xen/common/event_channel.c Fri Jul 04 15:13:53 2008 +0800 @@ -536,7 +536,7 @@ } -void evtchn_set_pending(struct vcpu *v, int port) +int evtchn_set_pending(struct vcpu *v, int port) { struct domain *d = v->domain; @@ -548,7 +548,7 @@ */ if ( test_and_set_bit(port, &shared_info(d, evtchn_pending)) ) - return; + return 1; if ( !test_bit (port, &shared_info(d, evtchn_mask)) && !test_and_set_bit(port / BITS_PER_GUEST_LONG(d), @@ -570,6 +570,8 @@ vcpu_unblock(v); } } + + return 0; } @@ -610,7 +612,7 @@ } -void send_guest_pirq(struct domain *d, int pirq) +int send_guest_pirq(struct domain *d, int pirq) { int port = d->pirq_to_evtchn[pirq]; struct evtchn *chn; @@ -618,7 +620,7 @@ ASSERT(port != 0); chn = evtchn_from_port(d, port); - evtchn_set_pending(d->vcpu[chn->notify_vcpu_id], port); + return evtchn_set_pending(d->vcpu[chn->notify_vcpu_id], port); } diff -r ec6d1ed854fb xen/drivers/passthrough/io.c --- a/xen/drivers/passthrough/io.c Thu Jul 03 10:47:27 2008 +0800 +++ b/xen/drivers/passthrough/io.c Fri Jul 04 15:13:53 2008 +0800 @@ -220,15 +220,26 @@ { struct hvm_irq_dpci *hvm_irq_dpci = d->arch.hvm_domain.irq.dpci; int pirq; + unsigned long flags; + irq_desc_t *desc; if ( !iommu_enabled || (hvm_irq_dpci == NULL) ) return; pirq = hvm_irq_dpci->msi_gvec_pirq[vector]; + if ( ( pirq >= 0 ) && (pirq < NR_PIRQS) && (hvm_irq_dpci->mirq[pirq].flags & HVM_IRQ_DPCI_VALID) && (hvm_irq_dpci->mirq[pirq].flags & HVM_IRQ_DPCI_MSI) ) - pirq_guest_eoi(d, pirq); + { + int vec; + vec = domain_irq_to_vector(d, pirq); + desc = &irq_desc[vec]; + + spin_lock_irqsave(&desc->lock, flags); + desc->status &= ~IRQ_INPROGRESS; + spin_unlock_irqrestore(&desc->lock, flags); + } } void hvm_dpci_eoi(struct domain *d, unsigned int guest_gsi, diff -r ec6d1ed854fb xen/include/xen/event.h --- a/xen/include/xen/event.h Thu Jul 03 10:47:27 2008 +0800 +++ b/xen/include/xen/event.h Fri Jul 04 15:13:53 2008 +0800 @@ -16,7 +16,7 @@ #include #include -void evtchn_set_pending(struct vcpu *v, int port); +int evtchn_set_pending(struct vcpu *v, int port); /* * send_guest_vcpu_virq: Notify guest via a per-VCPU VIRQ. @@ -37,7 +37,7 @@ * @d: Domain to which physical IRQ should be sent * @pirq: Physical IRQ number */ -void send_guest_pirq(struct domain *d, int pirq); +int send_guest_pirq(struct domain *d, int pirq); /* Send a notification from a local event-channel port. */ long evtchn_send(unsigned int lport);