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(
static DEFINE_PER_CPU(struct pending_eoi, pending_eoi[NR_VECTORS]);
#define pending_eoi_sp(p) ((p)[NR_VECTORS-1].vector)
+static struct timer irq_guest_eoi_timer[NR_IRQS];
+static void irq_guest_eoi_timer_fn(void *data)
+{
+ irq_desc_t *desc = data;
+ unsigned vector = desc - irq_desc;
+ unsigned long flags;
+
+ spin_lock_irqsave(&desc->lock, flags);
+ desc->status &= ~IRQ_INPROGRESS;
+ desc->handler->enable(vector);
+ spin_unlock_irqrestore(&desc->lock, flags);
+}
+
static void __do_IRQ_guest(int vector)
{
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, already_pending = 0;
struct pending_eoi *peoi = this_cpu(pending_eoi);
if ( unlikely(action->nr_guests == 0) )
if ( (action->ack_type != ACKTYPE_NONE) &&
!test_and_set_bit(irq, d->pirq_mask) )
action->in_flight++;
- if (!hvm_do_IRQ_dpci(d, irq))
- send_guest_pirq(d, irq);
+ if ( hvm_do_IRQ_dpci(d, irq) )
+ {
+ if ( action->ack_type == ACKTYPE_NONE )
+ {
+ already_pending += !!(desc->status & IRQ_INPROGRESS);
+ desc->status |= IRQ_INPROGRESS; /* cleared during hvm eoi */
+ }
+ }
+ else if ( send_guest_pirq(d, irq) &&
+ (action->ack_type == ACKTYPE_NONE) )
+ {
+ already_pending++;
+ }
+ }
+ if ( already_pending == action->nr_guests )
+ {
+ desc->handler->disable(vector);
+ stop_timer(&irq_guest_eoi_timer[vector]);
+ init_timer(&irq_guest_eoi_timer[vector],
+ irq_guest_eoi_timer_fn, desc, smp_processor_id());
+ set_timer(&irq_guest_eoi_timer[vector], NOW() + MILLISECS(1));
}
}
desc->action = NULL;
xfree(action);
desc->status &= ~IRQ_GUEST;
+ desc->status &= ~IRQ_INPROGRESS;
+ kill_timer(&irq_guest_eoi_timer[vector]);
desc->handler->shutdown(vector);
out:
goto out; \
} while ( 0 )
+static int evtchn_set_pending(struct vcpu *v, int port);
static int virq_is_global(int virq)
{
}
-void evtchn_set_pending(struct vcpu *v, int port)
+static int evtchn_set_pending(struct vcpu *v, int port)
{
struct domain *d = v->domain;
*/
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),
vcpu_unblock(v);
}
}
+
+ return 0;
}
}
-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;
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);
}
* PIC) and we need to detect that.
*/
set_bit(mirq, dpci->dirq_mask);
- if ( !test_bit(_HVM_IRQ_DPCI_MSI, &dpci->mirq[mirq].flags) )
- set_timer(&dpci->hvm_timer[domain_irq_to_vector(d, mirq)],
- NOW() + PT_IRQ_TIME_OUT);
+ if ( !test_bit(_HVM_IRQ_DPCI_MSI, &dpci->mirq[mirq].flags) )
+ set_timer(&dpci->hvm_timer[domain_irq_to_vector(d, mirq)],
+ NOW() + PT_IRQ_TIME_OUT);
vcpu_kick(d->vcpu[0]);
return 1;
{
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);
+
+ pirq_guest_eoi(d, pirq);
+ }
}
void hvm_dpci_eoi(struct domain *d, unsigned int guest_gsi,
#include <asm/bitops.h>
#include <asm/event.h>
-void evtchn_set_pending(struct vcpu *v, int port);
-
/*
* send_guest_vcpu_virq: Notify guest via a per-VCPU VIRQ.
* @v: VCPU to which virtual IRQ should be sent
* send_guest_pirq:
* @d: Domain to which physical IRQ should be sent
* @pirq: Physical IRQ number
+ * Returns TRUE if the delivery port was already pending.
*/
-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);