# HG changeset patch
# User kaf24@xxxxxxxxxxxxxxxxxxxx
# Node ID 1bce05ff1e5206f8d5b152130ded2f75296118c4
# Parent 8b40a2c75f8e2b562ee64807ba3d9575aa30e85c
IO-APIC update hypercall fixes. These are known to fix issues with
'pnpacpi' in domain 0. Incorporates the following three changesets
from xen-unstable.hg:
9589:e36892ad41444b5d2deb2fbea6b4ee2e52b7edac
"Clean up IO-APIC update tracing and demote to DPRINTK()."
9588:70837ac15cadf83e1fda27ae8468317afeb88ab0
"Fix remove_pin_at_irq()."
9586:f84a333d8aa6e1a25e2b73b07610e95007267f6a
"Robustify and add tracing to the IO-APIC update hypercall."
Signed-off-by: Keir Fraser <keir@xxxxxxxxxxxxx>
diff -r 8b40a2c75f8e -r 1bce05ff1e52 xen/arch/x86/io_apic.c
--- a/xen/arch/x86/io_apic.c Sat Apr 8 08:07:54 2006
+++ b/xen/arch/x86/io_apic.c Sat Apr 8 11:14:27 2006
@@ -75,6 +75,7 @@
static struct irq_pin_list {
int apic, pin, next;
} irq_2_pin[PIN_MAP_SIZE];
+static int irq_2_pin_free_entry = NR_IRQS;
int vector_irq[NR_VECTORS] __read_mostly = { [0 ... NR_VECTORS - 1] = -1};
@@ -85,20 +86,57 @@
*/
static void add_pin_to_irq(unsigned int irq, int apic, int pin)
{
- static int first_free_entry = NR_IRQS;
struct irq_pin_list *entry = irq_2_pin + irq;
- while (entry->next)
+ while (entry->next) {
+ BUG_ON((entry->apic == apic) && (entry->pin == pin));
entry = irq_2_pin + entry->next;
+ }
+
+ BUG_ON((entry->apic == apic) && (entry->pin == pin));
if (entry->pin != -1) {
- entry->next = first_free_entry;
+ if (irq_2_pin_free_entry >= PIN_MAP_SIZE)
+ panic("io_apic.c: whoops");
+ entry->next = irq_2_pin_free_entry;
entry = irq_2_pin + entry->next;
- if (++first_free_entry >= PIN_MAP_SIZE)
- panic("io_apic.c: whoops");
+ irq_2_pin_free_entry = entry->next;
+ entry->next = 0;
}
entry->apic = apic;
entry->pin = pin;
+}
+
+static void remove_pin_at_irq(unsigned int irq, int apic, int pin)
+{
+ struct irq_pin_list *entry, *prev;
+
+ for (entry = &irq_2_pin[irq]; ; entry = &irq_2_pin[entry->next]) {
+ if ((entry->apic == apic) && (entry->pin == pin))
+ break;
+ if (!entry->next)
+ BUG();
+ }
+
+ entry->pin = entry->apic = -1;
+
+ if (entry != &irq_2_pin[irq]) {
+ /* Removed entry is not at head of list. */
+ prev = &irq_2_pin[irq];
+ while (&irq_2_pin[prev->next] != entry)
+ prev = &irq_2_pin[prev->next];
+ prev->next = entry->next;
+ entry->next = irq_2_pin_free_entry;
+ irq_2_pin_free_entry = entry - irq_2_pin;
+ } else if (entry->next != 0) {
+ /* Removed entry is at head of multi-item list. */
+ prev = entry;
+ entry = &irq_2_pin[entry->next];
+ *prev = *entry;
+ entry->pin = entry->apic = -1;
+ entry->next = irq_2_pin_free_entry;
+ irq_2_pin_free_entry = entry - irq_2_pin;
+ }
}
/*
@@ -958,6 +996,10 @@
irq_2_pin[i].pin = -1;
irq_2_pin[i].next = 0;
}
+
+ /* Initialise dynamic irq_2_pin free list. */
+ for (i = NR_IRQS; i < PIN_MAP_SIZE; i++)
+ irq_2_pin[i].next = i + 1;
/*
* The number of IO-APIC IRQ registers (== #pins):
@@ -1852,11 +1894,17 @@
return 0;
}
+#define WARN_BOGUS_WRITE(f, a...) \
+ DPRINTK("\n%s: apic=%d, pin=%d, old_irq=%d, new_irq=%d\n" \
+ "%s: old_entry=%08x, new_entry=%08x\n" \
+ "%s: " f, __FUNCTION__, apic, pin, old_irq, new_irq, \
+ __FUNCTION__, *(u32 *)&old_rte, *(u32 *)&new_rte, \
+ __FUNCTION__ , ##a )
+
int ioapic_guest_write(unsigned long physbase, unsigned int reg, u32 val)
{
- int apic, pin, irq;
- struct IO_APIC_route_entry rte = { 0 };
- struct irq_pin_list *entry;
+ int apic, pin, old_irq = -1, new_irq = -1;
+ struct IO_APIC_route_entry old_rte = { 0 }, new_rte = { 0 };
unsigned long flags;
if ( (apic = ioapic_physbase_to_id(physbase)) < 0 )
@@ -1868,8 +1916,9 @@
pin = (reg - 0x10) >> 1;
- *(u32 *)&rte = val;
- rte.dest.logical.logical_dest = cpu_mask_to_apicid(TARGET_CPUS);
+ /* Write first half from guest; second half is target info. */
+ *(u32 *)&new_rte = val;
+ new_rte.dest.logical.logical_dest = cpu_mask_to_apicid(TARGET_CPUS);
/*
* What about weird destination types?
@@ -1879,7 +1928,7 @@
* ExtINT: Ignore? Linux only asserts this at start of day.
* For now, print a message and return an error. We can fix up on demand.
*/
- if ( rte.delivery_mode > dest_LowestPrio )
+ if ( new_rte.delivery_mode > dest_LowestPrio )
{
printk("ERROR: Attempt to write weird IOAPIC destination mode!\n");
printk(" APIC=%d/%d, lo-reg=%x\n", apic, pin, val);
@@ -1890,36 +1939,69 @@
* The guest does not know physical APIC arrangement (flat vs. cluster).
* Apply genapic conventions for this platform.
*/
- rte.delivery_mode = INT_DELIVERY_MODE;
- rte.dest_mode = INT_DEST_MODE;
-
- if ( rte.vector >= FIRST_DEVICE_VECTOR )
- {
- /* Is there a valid irq mapped to this vector? */
- irq = vector_irq[rte.vector];
- if ( !IO_APIC_IRQ(irq) )
+ new_rte.delivery_mode = INT_DELIVERY_MODE;
+ new_rte.dest_mode = INT_DEST_MODE;
+
+ spin_lock_irqsave(&ioapic_lock, flags);
+
+ /* Read first (interesting) half of current routing entry. */
+ *(u32 *)&old_rte = io_apic_read(apic, 0x10 + 2 * pin);
+
+ /* No change to the first half of the routing entry? Bail quietly. */
+ if ( *(u32 *)&old_rte == *(u32 *)&new_rte )
+ {
+ spin_unlock_irqrestore(&ioapic_lock, flags);
+ return 0;
+ }
+
+ if ( old_rte.vector >= FIRST_DEVICE_VECTOR )
+ old_irq = vector_irq[old_rte.vector];
+ if ( new_rte.vector >= FIRST_DEVICE_VECTOR )
+ new_irq = vector_irq[new_rte.vector];
+
+ if ( (old_irq != new_irq) && (old_irq != -1) && IO_APIC_IRQ(old_irq) )
+ {
+ if ( irq_desc[IO_APIC_VECTOR(old_irq)].action )
+ {
+ WARN_BOGUS_WRITE("Attempt to remove IO-APIC pin of in-use IRQ!\n");
+ spin_unlock_irqrestore(&ioapic_lock, flags);
return 0;
-
+ }
+
+ remove_pin_at_irq(old_irq, apic, pin);
+ }
+
+ if ( (new_irq != -1) && IO_APIC_IRQ(new_irq) )
+ {
+ if ( irq_desc[IO_APIC_VECTOR(new_irq)].action )
+ {
+ WARN_BOGUS_WRITE("Attempt to %s IO-APIC pin for in-use IRQ!\n",
+ (old_irq != new_irq) ? "add" : "modify");
+ spin_unlock_irqrestore(&ioapic_lock, flags);
+ return 0;
+ }
+
/* Set the correct irq-handling type. */
- irq_desc[IO_APIC_VECTOR(irq)].handler = rte.trigger ?
+ irq_desc[IO_APIC_VECTOR(new_irq)].handler = new_rte.trigger ?
&ioapic_level_type: &ioapic_edge_type;
-
- /* Record the pin<->irq mapping. */
- for ( entry = &irq_2_pin[irq]; ; entry = &irq_2_pin[entry->next] )
- {
- if ( (entry->apic == apic) && (entry->pin == pin) )
- break;
- if ( !entry->next )
- {
- add_pin_to_irq(irq, apic, pin);
- break;
- }
- }
- }
-
- spin_lock_irqsave(&ioapic_lock, flags);
- io_apic_write(apic, 0x10 + 2 * pin, *(((int *)&rte) + 0));
- io_apic_write(apic, 0x11 + 2 * pin, *(((int *)&rte) + 1));
+
+ if ( old_irq != new_irq )
+ add_pin_to_irq(new_irq, apic, pin);
+
+ /* Mask iff level triggered. */
+ new_rte.mask = new_rte.trigger;
+ }
+ else if ( !new_rte.mask )
+ {
+ /* This pin leads nowhere but the guest has not masked it. */
+ WARN_BOGUS_WRITE("Installing bogus unmasked IO-APIC entry!\n");
+ new_rte.mask = 1;
+ }
+
+
+ io_apic_write(apic, 0x10 + 2 * pin, *(((int *)&new_rte) + 0));
+ io_apic_write(apic, 0x11 + 2 * pin, *(((int *)&new_rte) + 1));
+
spin_unlock_irqrestore(&ioapic_lock, flags);
return 0;
_______________________________________________
Xen-changelog mailing list
Xen-changelog@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-changelog
|