]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - drivers/xen/events.c
xen: allocate irq descs on any NUMA node
[karo-tx-linux.git] / drivers / xen / events.c
index 32269bcbd88cfc33ce85ac04ebb964d9b26aa5e4..0009e489272c293800ba0ff6ecfd58f5c9dec7fd 100644 (file)
@@ -29,6 +29,7 @@
 #include <linux/bootmem.h>
 #include <linux/slab.h>
 #include <linux/irqnr.h>
+#include <linux/pci.h>
 
 #include <asm/desc.h>
 #include <asm/ptrace.h>
@@ -277,17 +278,17 @@ static void bind_evtchn_to_cpu(unsigned int chn, unsigned int cpu)
        cpumask_copy(irq_to_desc(irq)->affinity, cpumask_of(cpu));
 #endif
 
-       __clear_bit(chn, cpu_evtchn_mask(cpu_from_irq(irq)));
-       __set_bit(chn, cpu_evtchn_mask(cpu));
+       clear_bit(chn, cpu_evtchn_mask(cpu_from_irq(irq)));
+       set_bit(chn, cpu_evtchn_mask(cpu));
 
        irq_info[irq].cpu = cpu;
 }
 
 static void init_evtchn_cpu_bindings(void)
 {
+       int i;
 #ifdef CONFIG_SMP
        struct irq_desc *desc;
-       int i;
 
        /* By default all event channels notify CPU#0. */
        for_each_irq_desc(i, desc) {
@@ -295,7 +296,10 @@ static void init_evtchn_cpu_bindings(void)
        }
 #endif
 
-       memset(cpu_evtchn_mask(0), ~0, sizeof(cpu_evtchn_mask(0)));
+       for_each_possible_cpu(i)
+               memset(cpu_evtchn_mask(i),
+                      (i == 0) ? ~0 : 0, sizeof(struct cpu_evtchn_s));
+
 }
 
 static inline void clear_evtchn(int port)
@@ -419,7 +423,7 @@ static int find_unbound_irq(void)
        if (irq == start)
                goto no_irqs;
 
-       res = irq_alloc_desc_at(irq, 0);
+       res = irq_alloc_desc_at(irq, -1);
 
        if (WARN_ON(res != irq))
                return -1;
@@ -626,7 +630,7 @@ int xen_map_pirq_gsi(unsigned pirq, unsigned gsi, int shareable, char *name)
        if (identity_mapped_irq(gsi) || (!xen_initial_domain() &&
                                xen_pv_domain())) {
                irq = gsi;
-               irq_alloc_desc_at(irq, 0);
+               irq_alloc_desc_at(irq, -1);
        } else
                irq = find_unbound_irq();
 
@@ -656,9 +660,92 @@ out:
        return irq;
 }
 
+#ifdef CONFIG_PCI_MSI
+#include <linux/msi.h>
+#include "../pci/msi.h"
+
+void xen_allocate_pirq_msi(char *name, int *irq, int *pirq)
+{
+       spin_lock(&irq_mapping_update_lock);
+
+       *irq = find_unbound_irq();
+       if (*irq == -1)
+               goto out;
+
+       *pirq = find_unbound_pirq();
+       if (*pirq == -1)
+               goto out;
+
+       set_irq_chip_and_handler_name(*irq, &xen_pirq_chip,
+                                     handle_level_irq, name);
+
+       irq_info[*irq] = mk_pirq_info(0, *pirq, 0, 0);
+       pirq_to_irq[*pirq] = *irq;
+
+out:
+       spin_unlock(&irq_mapping_update_lock);
+}
+
+int xen_create_msi_irq(struct pci_dev *dev, struct msi_desc *msidesc, int type)
+{
+       int irq = -1;
+       struct physdev_map_pirq map_irq;
+       int rc;
+       int pos;
+       u32 table_offset, bir;
+
+       memset(&map_irq, 0, sizeof(map_irq));
+       map_irq.domid = DOMID_SELF;
+       map_irq.type = MAP_PIRQ_TYPE_MSI;
+       map_irq.index = -1;
+       map_irq.pirq = -1;
+       map_irq.bus = dev->bus->number;
+       map_irq.devfn = dev->devfn;
+
+       if (type == PCI_CAP_ID_MSIX) {
+               pos = pci_find_capability(dev, PCI_CAP_ID_MSIX);
+
+               pci_read_config_dword(dev, msix_table_offset_reg(pos),
+                                       &table_offset);
+               bir = (u8)(table_offset & PCI_MSIX_FLAGS_BIRMASK);
+
+               map_irq.table_base = pci_resource_start(dev, bir);
+               map_irq.entry_nr = msidesc->msi_attrib.entry_nr;
+       }
+
+       spin_lock(&irq_mapping_update_lock);
+
+       irq = find_unbound_irq();
+
+       if (irq == -1)
+               goto out;
+
+       rc = HYPERVISOR_physdev_op(PHYSDEVOP_map_pirq, &map_irq);
+       if (rc) {
+               printk(KERN_WARNING "xen map irq failed %d\n", rc);
+
+               irq_free_desc(irq);
+
+               irq = -1;
+               goto out;
+       }
+       irq_info[irq] = mk_pirq_info(0, map_irq.pirq, 0, map_irq.index);
+
+       set_irq_chip_and_handler_name(irq, &xen_pirq_chip,
+                       handle_level_irq,
+                       (type == PCI_CAP_ID_MSIX) ? "msi-x":"msi");
+
+out:
+       spin_unlock(&irq_mapping_update_lock);
+       return irq;
+}
+#endif
+
 int xen_destroy_irq(int irq)
 {
        struct irq_desc *desc;
+       struct physdev_unmap_pirq unmap_irq;
+       struct irq_info *info = info_for_irq(irq);
        int rc = -ENOENT;
 
        spin_lock(&irq_mapping_update_lock);
@@ -667,6 +754,15 @@ int xen_destroy_irq(int irq)
        if (!desc)
                goto out;
 
+       if (xen_initial_domain()) {
+               unmap_irq.pirq = info->u.pirq.pirq;
+               unmap_irq.domid = DOMID_SELF;
+               rc = HYPERVISOR_physdev_op(PHYSDEVOP_unmap_pirq, &unmap_irq);
+               if (rc) {
+                       printk(KERN_WARNING "unmap irq failed %d\n", rc);
+                       goto out;
+               }
+       }
        irq_info[irq] = mk_unbound_info();
 
        irq_free_desc(irq);
@@ -698,7 +794,7 @@ int bind_evtchn_to_irq(unsigned int evtchn)
                irq = find_unbound_irq();
 
                set_irq_chip_and_handler_name(irq, &xen_dynamic_chip,
-                                             handle_edge_irq, "event");
+                                             handle_fasteoi_irq, "event");
 
                evtchn_to_irq[evtchn] = irq;
                irq_info[irq] = mk_evtchn_info(evtchn);
@@ -746,7 +842,7 @@ static int bind_ipi_to_irq(unsigned int ipi, unsigned int cpu)
 }
 
 
-static int bind_virq_to_irq(unsigned int virq, unsigned int cpu)
+int bind_virq_to_irq(unsigned int virq, unsigned int cpu)
 {
        struct evtchn_bind_virq bind_virq;
        int evtchn, irq;
@@ -756,6 +852,11 @@ static int bind_virq_to_irq(unsigned int virq, unsigned int cpu)
        irq = per_cpu(virq_to_irq, cpu)[virq];
 
        if (irq == -1) {
+               irq = find_unbound_irq();
+
+               set_irq_chip_and_handler_name(irq, &xen_percpu_chip,
+                                             handle_percpu_irq, "virq");
+
                bind_virq.virq = virq;
                bind_virq.vcpu = cpu;
                if (HYPERVISOR_event_channel_op(EVTCHNOP_bind_virq,
@@ -763,11 +864,6 @@ static int bind_virq_to_irq(unsigned int virq, unsigned int cpu)
                        BUG();
                evtchn = bind_virq.port;
 
-               irq = find_unbound_irq();
-
-               set_irq_chip_and_handler_name(irq, &xen_percpu_chip,
-                                             handle_percpu_irq, "virq");
-
                evtchn_to_irq[evtchn] = irq;
                irq_info[irq] = mk_virq_info(evtchn, virq);
 
@@ -899,41 +995,75 @@ irqreturn_t xen_debug_interrupt(int irq, void *dev_id)
 {
        struct shared_info *sh = HYPERVISOR_shared_info;
        int cpu = smp_processor_id();
+       unsigned long *cpu_evtchn = cpu_evtchn_mask(cpu);
        int i;
        unsigned long flags;
        static DEFINE_SPINLOCK(debug_lock);
+       struct vcpu_info *v;
 
        spin_lock_irqsave(&debug_lock, flags);
 
-       printk("vcpu %d\n  ", cpu);
+       printk("\nvcpu %d\n  ", cpu);
 
        for_each_online_cpu(i) {
-               struct vcpu_info *v = per_cpu(xen_vcpu, i);
-               printk("%d: masked=%d pending=%d event_sel %08lx\n  ", i,
-                       (get_irq_regs() && i == cpu) ? xen_irqs_disabled(get_irq_regs()) : v->evtchn_upcall_mask,
-                       v->evtchn_upcall_pending,
-                       v->evtchn_pending_sel);
+               int pending;
+               v = per_cpu(xen_vcpu, i);
+               pending = (get_irq_regs() && i == cpu)
+                       ? xen_irqs_disabled(get_irq_regs())
+                       : v->evtchn_upcall_mask;
+               printk("%d: masked=%d pending=%d event_sel %0*lx\n  ", i,
+                      pending, v->evtchn_upcall_pending,
+                      (int)(sizeof(v->evtchn_pending_sel)*2),
+                      v->evtchn_pending_sel);
+       }
+       v = per_cpu(xen_vcpu, cpu);
+
+       printk("\npending:\n   ");
+       for (i = ARRAY_SIZE(sh->evtchn_pending)-1; i >= 0; i--)
+               printk("%0*lx%s", (int)sizeof(sh->evtchn_pending[0])*2,
+                      sh->evtchn_pending[i],
+                      i % 8 == 0 ? "\n   " : " ");
+       printk("\nglobal mask:\n   ");
+       for (i = ARRAY_SIZE(sh->evtchn_mask)-1; i >= 0; i--)
+               printk("%0*lx%s",
+                      (int)(sizeof(sh->evtchn_mask[0])*2),
+                      sh->evtchn_mask[i],
+                      i % 8 == 0 ? "\n   " : " ");
+
+       printk("\nglobally unmasked:\n   ");
+       for (i = ARRAY_SIZE(sh->evtchn_mask)-1; i >= 0; i--)
+               printk("%0*lx%s", (int)(sizeof(sh->evtchn_mask[0])*2),
+                      sh->evtchn_pending[i] & ~sh->evtchn_mask[i],
+                      i % 8 == 0 ? "\n   " : " ");
+
+       printk("\nlocal cpu%d mask:\n   ", cpu);
+       for (i = (NR_EVENT_CHANNELS/BITS_PER_LONG)-1; i >= 0; i--)
+               printk("%0*lx%s", (int)(sizeof(cpu_evtchn[0])*2),
+                      cpu_evtchn[i],
+                      i % 8 == 0 ? "\n   " : " ");
+
+       printk("\nlocally unmasked:\n   ");
+       for (i = ARRAY_SIZE(sh->evtchn_mask)-1; i >= 0; i--) {
+               unsigned long pending = sh->evtchn_pending[i]
+                       & ~sh->evtchn_mask[i]
+                       & cpu_evtchn[i];
+               printk("%0*lx%s", (int)(sizeof(sh->evtchn_mask[0])*2),
+                      pending, i % 8 == 0 ? "\n   " : " ");
        }
-       printk("pending:\n   ");
-       for(i = ARRAY_SIZE(sh->evtchn_pending)-1; i >= 0; i--)
-               printk("%08lx%s", sh->evtchn_pending[i],
-                       i % 8 == 0 ? "\n   " : " ");
-       printk("\nmasks:\n   ");
-       for(i = ARRAY_SIZE(sh->evtchn_mask)-1; i >= 0; i--)
-               printk("%08lx%s", sh->evtchn_mask[i],
-                       i % 8 == 0 ? "\n   " : " ");
-
-       printk("\nunmasked:\n   ");
-       for(i = ARRAY_SIZE(sh->evtchn_mask)-1; i >= 0; i--)
-               printk("%08lx%s", sh->evtchn_pending[i] & ~sh->evtchn_mask[i],
-                       i % 8 == 0 ? "\n   " : " ");
 
        printk("\npending list:\n");
-       for(i = 0; i < NR_EVENT_CHANNELS; i++) {
+       for (i = 0; i < NR_EVENT_CHANNELS; i++) {
                if (sync_test_bit(i, sh->evtchn_pending)) {
-                       printk("  %d: event %d -> irq %d\n",
+                       int word_idx = i / BITS_PER_LONG;
+                       printk("  %d: event %d -> irq %d%s%s%s\n",
                               cpu_from_evtchn(i), i,
-                              evtchn_to_irq[i]);
+                              evtchn_to_irq[i],
+                              sync_test_bit(word_idx, &v->evtchn_pending_sel)
+                                            ? "" : " l2-clear",
+                              !sync_test_bit(i, sh->evtchn_mask)
+                                            ? "" : " globally-masked",
+                              sync_test_bit(i, cpu_evtchn)
+                                            ? "" : " locally-masked");
                }
        }
 
@@ -984,6 +1114,9 @@ static void __xen_evtchn_do_upcall(void)
                                int irq = evtchn_to_irq[port];
                                struct irq_desc *desc;
 
+                               mask_evtchn(port);
+                               clear_evtchn(port);
+
                                if (irq != -1) {
                                        desc = irq_to_desc(irq);
                                        if (desc)
@@ -1121,10 +1254,10 @@ static void ack_dynirq(unsigned int irq)
 {
        int evtchn = evtchn_from_irq(irq);
 
-       move_native_irq(irq);
+       move_masked_irq(irq);
 
        if (VALID_EVTCHN(evtchn))
-               clear_evtchn(evtchn);
+               unmask_evtchn(evtchn);
 }
 
 static int retrigger_dynirq(unsigned int irq)
@@ -1169,9 +1302,6 @@ static void restore_cpu_virqs(unsigned int cpu)
                evtchn_to_irq[evtchn] = irq;
                irq_info[irq] = mk_virq_info(evtchn, virq);
                bind_evtchn_to_cpu(evtchn, cpu);
-
-               /* Ready for use. */
-               unmask_evtchn(evtchn);
        }
 }
 
@@ -1197,10 +1327,6 @@ static void restore_cpu_ipis(unsigned int cpu)
                evtchn_to_irq[evtchn] = irq;
                irq_info[irq] = mk_ipi_info(evtchn, ipi);
                bind_evtchn_to_cpu(evtchn, cpu);
-
-               /* Ready for use. */
-               unmask_evtchn(evtchn);
-
        }
 }
 
@@ -1260,6 +1386,7 @@ void xen_poll_irq(int irq)
 void xen_irq_resume(void)
 {
        unsigned int cpu, irq, evtchn;
+       struct irq_desc *desc;
 
        init_evtchn_cpu_bindings();
 
@@ -1278,6 +1405,23 @@ void xen_irq_resume(void)
                restore_cpu_virqs(cpu);
                restore_cpu_ipis(cpu);
        }
+
+       /*
+        * Unmask any IRQF_NO_SUSPEND IRQs which are enabled. These
+        * are not handled by the IRQ core.
+        */
+       for_each_irq_desc(irq, desc) {
+               if (!desc->action || !(desc->action->flags & IRQF_NO_SUSPEND))
+                       continue;
+               if (desc->status & IRQ_DISABLED)
+                       continue;
+
+               evtchn = evtchn_from_irq(irq);
+               if (evtchn == -1)
+                       continue;
+
+               unmask_evtchn(evtchn);
+       }
 }
 
 static struct irq_chip xen_dynamic_chip __read_mostly = {
@@ -1287,7 +1431,7 @@ static struct irq_chip xen_dynamic_chip __read_mostly = {
        .mask           = disable_dynirq,
        .unmask         = enable_dynirq,
 
-       .ack            = ack_dynirq,
+       .eoi            = ack_dynirq,
        .set_affinity   = set_affinity_irq,
        .retrigger      = retrigger_dynirq,
 };
@@ -1403,5 +1547,7 @@ void __init xen_init_IRQ(void)
                pci_xen_hvm_init();
        } else {
                irq_ctx_init(smp_processor_id());
+               if (xen_initial_domain())
+                       xen_setup_pirqs();
        }
 }