aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrew Cooper <andrew.cooper3@citrix.com>2011-08-31 15:37:57 +0100
committerAndrew Cooper <andrew.cooper3@citrix.com>2011-08-31 15:37:57 +0100
commitd9d0296d46ae46b4c5d7ab14719d1787ece1779f (patch)
tree59c1fd34428f7e9ae8b2bc39ae6fcbdb88f0e575
parent3199999ae15b29a00db8d1a0d90200f408b41061 (diff)
downloadxen-d9d0296d46ae46b4c5d7ab14719d1787ece1779f.tar.gz
xen-d9d0296d46ae46b4c5d7ab14719d1787ece1779f.tar.bz2
xen-d9d0296d46ae46b4c5d7ab14719d1787ece1779f.zip
IRQ: manually EOI migrating line interrupts
When migrating IO-APIC line level interrupts between PCPUs, the migration code rewrites the IO-APIC entry to point to the new CPU/Vector before EOI'ing it. The EOI process says that EOI'ing the Local APIC will cause a broadcast with the vector number, which the IO-APIC must listen to to clear the IRR and Status bits. In the case of migrating, the IO-APIC has already been reprogrammed so the EOI broadcast with the old vector fails to match the new vector, leaving the IO-APIC with an outstanding vector, preventing any more use of that line interrupt. This causes a lockup especially when your root device is using PCI INTA (megaraid_sas driver *ehem*) However, the problem is mostly hidden because send_cleanup_vector() causes a cleanup of all moving vectors on the current PCPU in such a way which does not cause the problem, and if the problem has occured, the writes it makes to the IO-APIC clears the IRR and Status bits which unlocks the problem. This fix is distinctly a temporary hack, waiting on a cleanup of the irq code. It checks for the edge case where we have moved the irq, and manually EOI's the old vector with the IO-APIC which correctly clears the IRR and Status bits. Also, it protects the code which updates irq_cfg by disabling interrupts. Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com> xen-unstable changeset: 23805:7048810180de xen-unstable date: Wed Aug 31 15:19:24 2011 +0100
-rw-r--r--xen/arch/x86/hpet.c2
-rw-r--r--xen/arch/x86/hvm/vmx/vpmu_core2.c3
-rw-r--r--xen/arch/x86/i8259.c2
-rw-r--r--xen/arch/x86/io_apic.c20
-rw-r--r--xen/arch/x86/irq.c17
-rw-r--r--xen/drivers/passthrough/amd/iommu_init.c2
-rw-r--r--xen/drivers/passthrough/vtd/iommu.c2
-rw-r--r--xen/include/xen/irq.h2
8 files changed, 31 insertions, 19 deletions
diff --git a/xen/arch/x86/hpet.c b/xen/arch/x86/hpet.c
index 99213f5da5..ca6a0297a0 100644
--- a/xen/arch/x86/hpet.c
+++ b/xen/arch/x86/hpet.c
@@ -297,7 +297,7 @@ static void hpet_msi_ack(unsigned int irq)
ack_APIC_irq();
}
-static void hpet_msi_end(unsigned int irq)
+static void hpet_msi_end(unsigned int irq, u8 vector)
{
}
diff --git a/xen/arch/x86/hvm/vmx/vpmu_core2.c b/xen/arch/x86/hvm/vmx/vpmu_core2.c
index 31aa1170c6..69de4ecadd 100644
--- a/xen/arch/x86/hvm/vmx/vpmu_core2.c
+++ b/xen/arch/x86/hvm/vmx/vpmu_core2.c
@@ -55,7 +55,8 @@ static void check_pmc_quirk(void)
is_pmc_quirk = 0;
if ( family == 6 )
{
- if ( cpu_model == 46 || cpu_model == 26 )
+ if ( cpu_model == 47 || cpu_model == 46 || cpu_model == 42 ||
+ cpu_model == 26 )
is_pmc_quirk = 1;
}
}
diff --git a/xen/arch/x86/i8259.c b/xen/arch/x86/i8259.c
index 14dcb289e7..37e427225e 100644
--- a/xen/arch/x86/i8259.c
+++ b/xen/arch/x86/i8259.c
@@ -91,7 +91,7 @@ static unsigned int startup_8259A_irq(unsigned int irq)
return 0; /* never anything pending */
}
-static void end_8259A_irq(unsigned int irq)
+static void end_8259A_irq(unsigned int irq, u8 vector)
{
if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
enable_8259A_irq(irq);
diff --git a/xen/arch/x86/io_apic.c b/xen/arch/x86/io_apic.c
index aab435504f..8b883dafe9 100644
--- a/xen/arch/x86/io_apic.c
+++ b/xen/arch/x86/io_apic.c
@@ -1651,7 +1651,7 @@ static void mask_and_ack_level_ioapic_irq (unsigned int irq)
}
}
-static void end_level_ioapic_irq (unsigned int irq)
+static void end_level_ioapic_irq (unsigned int irq, u8 vector)
{
unsigned long v;
int i;
@@ -1700,6 +1700,14 @@ static void end_level_ioapic_irq (unsigned int irq)
*/
i = IO_APIC_VECTOR(irq);
+ /* Manually EOI the old vector if we are moving to the new */
+ if ( vector && i != vector )
+ {
+ int ioapic;
+ for (ioapic = 0; ioapic < nr_ioapics; ioapic++)
+ io_apic_eoi(ioapic, i);
+ }
+
v = apic_read(APIC_TMR + ((i & ~0x1f) >> 1));
ack_APIC_irq();
@@ -1724,9 +1732,9 @@ static void disable_edge_ioapic_irq(unsigned int irq)
{
}
-static void end_edge_ioapic_irq(unsigned int irq)
- {
- }
+static void end_edge_ioapic_irq(unsigned int irq, u8 vector)
+{
+}
/*
* Level and edge triggered IO-APIC interrupts need different handling,
@@ -1775,7 +1783,7 @@ static void ack_msi_irq(unsigned int irq)
ack_APIC_irq(); /* ACKTYPE_NONE */
}
-static void end_msi_irq(unsigned int irq)
+static void end_msi_irq(unsigned int irq, u8 vector)
{
if ( !msi_maskable_irq(irq_desc[irq].msi_desc) )
ack_APIC_irq(); /* ACKTYPE_EOI */
@@ -1828,7 +1836,7 @@ static void ack_lapic_irq(unsigned int irq)
ack_APIC_irq();
}
-static void end_lapic_irq(unsigned int irq) { /* nothing */ }
+#define end_lapic_irq end_edge_ioapic_irq
static hw_irq_controller lapic_irq_type = {
.typename = "local-APIC-edge",
diff --git a/xen/arch/x86/irq.c b/xen/arch/x86/irq.c
index 3a4d3bfef4..60cb65d121 100644
--- a/xen/arch/x86/irq.c
+++ b/xen/arch/x86/irq.c
@@ -307,6 +307,7 @@ static void __do_IRQ_guest(int vector);
void no_action(int cpl, void *dev_id, struct cpu_user_regs *regs) { }
static void enable_none(unsigned int vector) { }
+static void end_none(unsigned int irq, u8 vector) { }
static unsigned int startup_none(unsigned int vector) { return 0; }
static void disable_none(unsigned int vector) { }
static void ack_none(unsigned int irq)
@@ -315,7 +316,6 @@ static void ack_none(unsigned int irq)
}
#define shutdown_none disable_none
-#define end_none enable_none
hw_irq_controller no_irq_type = {
"none",
@@ -345,6 +345,7 @@ int __assign_irq_vector(int irq, struct irq_cfg *cfg, cpumask_t mask)
static int current_vector = FIRST_DYNAMIC_VECTOR, current_offset = 0;
unsigned int old_vector;
int cpu, err;
+ unsigned long flags;
cpumask_t tmp_mask;
if ((cfg->move_in_progress) || cfg->move_cleanup_count)
@@ -392,6 +393,7 @@ next:
/* Found one! */
current_vector = vector;
current_offset = offset;
+ local_irq_save(flags);
if (old_vector) {
cfg->move_in_progress = 1;
cpus_copy(cfg->old_domain, cfg->domain);
@@ -405,6 +407,7 @@ next:
if (IO_APIC_IRQ(irq))
irq_vector[irq] = vector;
err = 0;
+ local_irq_restore(flags);
break;
}
return err;
@@ -599,7 +602,7 @@ asmlinkage void do_IRQ(struct cpu_user_regs *regs)
desc->status &= ~IRQ_INPROGRESS;
out:
- desc->handler->end(irq);
+ desc->handler->end(irq, regs->entry_vector);
out_no_end:
spin_unlock(&desc->lock);
irq_exit();
@@ -815,7 +818,7 @@ static void irq_guest_eoi_timer_fn(void *data)
switch ( action->ack_type )
{
case ACKTYPE_UNMASK:
- desc->handler->end(irq);
+ desc->handler->end(irq, 0);
break;
case ACKTYPE_EOI:
cpu_eoi_map = action->cpu_eoi_map;
@@ -846,7 +849,7 @@ static void __do_IRQ_guest(int irq)
/* An interrupt may slip through while freeing an ACKTYPE_EOI irq. */
ASSERT(action->ack_type == ACKTYPE_EOI);
ASSERT(desc->status & IRQ_DISABLED);
- desc->handler->end(irq);
+ desc->handler->end(irq, vector);
return;
}
@@ -957,7 +960,7 @@ static void flush_ready_eoi(void)
ASSERT(irq > 0);
desc = irq_to_desc(irq);
spin_lock(&desc->lock);
- desc->handler->end(irq);
+ desc->handler->end(irq, peoi[sp].vector);
spin_unlock(&desc->lock);
}
@@ -1038,7 +1041,7 @@ static void __pirq_guest_eoi(struct domain *d, int pirq)
if ( action->ack_type == ACKTYPE_UNMASK )
{
ASSERT(cpus_empty(action->cpu_eoi_map));
- desc->handler->end(irq);
+ desc->handler->end(irq, 0);
spin_unlock_irq(&desc->lock);
return;
}
@@ -1298,7 +1301,7 @@ static irq_guest_action_t *__pirq_guest_unbind(
case ACKTYPE_UNMASK:
if ( test_and_clear_bit(pirq, d->pirq_mask) &&
(--action->in_flight == 0) )
- desc->handler->end(irq);
+ desc->handler->end(irq, 0);
break;
case ACKTYPE_EOI:
/* NB. If #guests == 0 then we clear the eoi_map later on. */
diff --git a/xen/drivers/passthrough/amd/iommu_init.c b/xen/drivers/passthrough/amd/iommu_init.c
index 634930f488..4f420430ea 100644
--- a/xen/drivers/passthrough/amd/iommu_init.c
+++ b/xen/drivers/passthrough/amd/iommu_init.c
@@ -437,7 +437,7 @@ static unsigned int iommu_msi_startup(unsigned int irq)
return 0;
}
-static void iommu_msi_end(unsigned int irq)
+static void iommu_msi_end(unsigned int irq, u8 vector)
{
iommu_msi_unmask(irq);
ack_APIC_irq();
diff --git a/xen/drivers/passthrough/vtd/iommu.c b/xen/drivers/passthrough/vtd/iommu.c
index eb003384ba..d5d5bfa63f 100644
--- a/xen/drivers/passthrough/vtd/iommu.c
+++ b/xen/drivers/passthrough/vtd/iommu.c
@@ -967,7 +967,7 @@ static unsigned int dma_msi_startup(unsigned int irq)
return 0;
}
-static void dma_msi_end(unsigned int irq)
+static void dma_msi_end(unsigned int irq, u8 vector)
{
dma_msi_unmask(irq);
ack_APIC_irq();
diff --git a/xen/include/xen/irq.h b/xen/include/xen/irq.h
index dae4a2a122..34c4aefd10 100644
--- a/xen/include/xen/irq.h
+++ b/xen/include/xen/irq.h
@@ -43,7 +43,7 @@ struct hw_interrupt_type {
void (*enable)(unsigned int irq);
void (*disable)(unsigned int irq);
void (*ack)(unsigned int irq);
- void (*end)(unsigned int irq);
+ void (*end)(unsigned int irq, u8 vector);
void (*set_affinity)(unsigned int irq, cpumask_t mask);
};