]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/pci/intr_remapping.c
VT-d: handle Invalidation Queue Error to avoid system hang
[karo-tx-linux.git] / drivers / pci / intr_remapping.c
index 2de5a3238c947be89213119d9cf468d2fa59e11d..45effc5726c0f63a55862c85eaa6d9cb4a38c706 100644 (file)
@@ -5,6 +5,7 @@
 #include <linux/pci.h>
 #include <linux/irq.h>
 #include <asm/io_apic.h>
+#include <asm/smp.h>
 #include <linux/intel-iommu.h>
 #include "intr_remapping.h"
 
@@ -19,17 +20,75 @@ struct irq_2_iommu {
        u8  irte_mask;
 };
 
-static struct irq_2_iommu irq_2_iommuX[NR_IRQS];
+#ifdef CONFIG_SPARSE_IRQ
+static struct irq_2_iommu *get_one_free_irq_2_iommu(int cpu)
+{
+       struct irq_2_iommu *iommu;
+       int node;
+
+       node = cpu_to_node(cpu);
+
+       iommu = kzalloc_node(sizeof(*iommu), GFP_ATOMIC, node);
+       printk(KERN_DEBUG "alloc irq_2_iommu on cpu %d node %d\n", cpu, node);
+
+       return iommu;
+}
 
 static struct irq_2_iommu *irq_2_iommu(unsigned int irq)
 {
-       return (irq < nr_irqs) ? irq_2_iommuX + irq : NULL;
+       struct irq_desc *desc;
+
+       desc = irq_to_desc(irq);
+
+       if (WARN_ON_ONCE(!desc))
+               return NULL;
+
+       return desc->irq_2_iommu;
+}
+
+static struct irq_2_iommu *irq_2_iommu_alloc_cpu(unsigned int irq, int cpu)
+{
+       struct irq_desc *desc;
+       struct irq_2_iommu *irq_iommu;
+
+       /*
+        * alloc irq desc if not allocated already.
+        */
+       desc = irq_to_desc_alloc_cpu(irq, cpu);
+       if (!desc) {
+               printk(KERN_INFO "can not get irq_desc for %d\n", irq);
+               return NULL;
+       }
+
+       irq_iommu = desc->irq_2_iommu;
+
+       if (!irq_iommu)
+               desc->irq_2_iommu = get_one_free_irq_2_iommu(cpu);
+
+       return desc->irq_2_iommu;
+}
+
+static struct irq_2_iommu *irq_2_iommu_alloc(unsigned int irq)
+{
+       return irq_2_iommu_alloc_cpu(irq, boot_cpu_id);
 }
 
+#else /* !CONFIG_SPARSE_IRQ */
+
+static struct irq_2_iommu irq_2_iommuX[NR_IRQS];
+
+static struct irq_2_iommu *irq_2_iommu(unsigned int irq)
+{
+       if (irq < nr_irqs)
+               return &irq_2_iommuX[irq];
+
+       return NULL;
+}
 static struct irq_2_iommu *irq_2_iommu_alloc(unsigned int irq)
 {
        return irq_2_iommu(irq);
 }
+#endif
 
 static DEFINE_SPINLOCK(irq_2_ir_lock);
 
@@ -86,9 +145,11 @@ int alloc_irte(struct intel_iommu *iommu, int irq, u16 count)
        if (!count)
                return -1;
 
+#ifndef CONFIG_SPARSE_IRQ
        /* protect irq_2_iommu_alloc later */
        if (irq >= nr_irqs)
                return -1;
+#endif
 
        /*
         * start the IRTE search from index 0.
@@ -130,6 +191,12 @@ int alloc_irte(struct intel_iommu *iommu, int irq, u16 count)
                table->base[i].present = 1;
 
        irq_iommu = irq_2_iommu_alloc(irq);
+       if (!irq_iommu) {
+               spin_unlock(&irq_2_ir_lock);
+               printk(KERN_ERR "can't allocate irq_2_iommu\n");
+               return -1;
+       }
+
        irq_iommu->iommu = iommu;
        irq_iommu->irte_index =  index;
        irq_iommu->sub_handle = 0;
@@ -140,7 +207,7 @@ int alloc_irte(struct intel_iommu *iommu, int irq, u16 count)
        return index;
 }
 
-static void qi_flush_iec(struct intel_iommu *iommu, int index, int mask)
+static int qi_flush_iec(struct intel_iommu *iommu, int index, int mask)
 {
        struct qi_desc desc;
 
@@ -148,7 +215,7 @@ static void qi_flush_iec(struct intel_iommu *iommu, int index, int mask)
                   | QI_IEC_SELECTIVE;
        desc.high = 0;
 
-       qi_submit_sync(&desc, iommu);
+       return qi_submit_sync(&desc, iommu);
 }
 
 int map_irq_to_irte_handle(int irq, u16 *sub_handle)
@@ -177,6 +244,12 @@ int set_irte_irq(int irq, struct intel_iommu *iommu, u16 index, u16 subhandle)
 
        irq_iommu = irq_2_iommu_alloc(irq);
 
+       if (!irq_iommu) {
+               spin_unlock(&irq_2_ir_lock);
+               printk(KERN_ERR "can't allocate irq_2_iommu\n");
+               return -1;
+       }
+
        irq_iommu->iommu = iommu;
        irq_iommu->irte_index = index;
        irq_iommu->sub_handle = subhandle;
@@ -210,6 +283,7 @@ int clear_irte_irq(int irq, struct intel_iommu *iommu, u16 index)
 
 int modify_irte(int irq, struct irte *irte_modified)
 {
+       int rc;
        int index;
        struct irte *irte;
        struct intel_iommu *iommu;
@@ -230,14 +304,15 @@ int modify_irte(int irq, struct irte *irte_modified)
        set_64bit((unsigned long *)irte, irte_modified->low | (1 << 1));
        __iommu_flush_cache(iommu, irte, sizeof(*irte));
 
-       qi_flush_iec(iommu, index, 0);
-
+       rc = qi_flush_iec(iommu, index, 0);
        spin_unlock(&irq_2_ir_lock);
-       return 0;
+
+       return rc;
 }
 
 int flush_irte(int irq)
 {
+       int rc;
        int index;
        struct intel_iommu *iommu;
        struct irq_2_iommu *irq_iommu;
@@ -253,10 +328,10 @@ int flush_irte(int irq)
 
        index = irq_iommu->irte_index + irq_iommu->sub_handle;
 
-       qi_flush_iec(iommu, index, irq_iommu->irte_mask);
+       rc = qi_flush_iec(iommu, index, irq_iommu->irte_mask);
        spin_unlock(&irq_2_ir_lock);
 
-       return 0;
+       return rc;
 }
 
 struct intel_iommu *map_ioapic_to_ir(int apic)
@@ -282,6 +357,7 @@ struct intel_iommu *map_dev_to_ir(struct pci_dev *dev)
 
 int free_irte(int irq)
 {
+       int rc = 0;
        int index, i;
        struct irte *irte;
        struct intel_iommu *iommu;
@@ -302,7 +378,7 @@ int free_irte(int irq)
        if (!irq_iommu->sub_handle) {
                for (i = 0; i < (1 << irq_iommu->irte_mask); i++)
                        set_64bit((unsigned long *)irte, 0);
-               qi_flush_iec(iommu, index, irq_iommu->irte_mask);
+               rc = qi_flush_iec(iommu, index, irq_iommu->irte_mask);
        }
 
        irq_iommu->iommu = NULL;
@@ -312,7 +388,7 @@ int free_irte(int irq)
 
        spin_unlock(&irq_2_ir_lock);
 
-       return 0;
+       return rc;
 }
 
 static void iommu_set_intr_remapping(struct intel_iommu *iommu, int mode)