diff options
author | Andrew Cooper <andrew.cooper3@citrix.com> | 2011-08-31 15:19:24 +0100 |
---|---|---|
committer | Andrew Cooper <andrew.cooper3@citrix.com> | 2011-08-31 15:19:24 +0100 |
commit | 12b6ea528fd6048bebcaee852d1b6be811907be3 (patch) | |
tree | d49822a6409db3602a8b945abc2f0ae9e1bc424d /xen/arch/x86/io_apic.c | |
parent | a81d0ee933c35323f32269dad37977934b6cbb28 (diff) | |
download | xen-12b6ea528fd6048bebcaee852d1b6be811907be3.tar.gz xen-12b6ea528fd6048bebcaee852d1b6be811907be3.tar.bz2 xen-12b6ea528fd6048bebcaee852d1b6be811907be3.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>
Diffstat (limited to 'xen/arch/x86/io_apic.c')
-rw-r--r-- | xen/arch/x86/io_apic.c | 17 |
1 files changed, 14 insertions, 3 deletions
diff --git a/xen/arch/x86/io_apic.c b/xen/arch/x86/io_apic.c index 471f7530e4..b333626791 100644 --- a/xen/arch/x86/io_apic.c +++ b/xen/arch/x86/io_apic.c @@ -1691,7 +1691,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; @@ -1740,6 +1740,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(); @@ -1763,7 +1771,10 @@ static void disable_edge_ioapic_irq(unsigned int irq) { } -#define end_edge_ioapic_irq disable_edge_ioapic_irq +static void end_edge_ioapic_irq(unsigned int irq, u8 vector) +{ +} + /* * Level and edge triggered IO-APIC interrupts need different handling, @@ -1812,7 +1823,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 */ |