Merge git://git.infradead.org/intel-iommu
[sfrench/cifs-2.6.git] / drivers / iommu / intel_irq_remapping.c
index 07d1a6848ad7bf7a1e5ac05098d7dd213ed64081..5709ae9c3e771d2f82a1bda2a23d500d8f4faffe 100644 (file)
@@ -32,8 +32,9 @@ struct hpet_scope {
 };
 
 #define IR_X2APIC_MODE(mode) (mode ? (1 << 11) : 0)
-#define IRTE_DEST(dest) ((x2apic_mode) ? dest : dest << 8)
+#define IRTE_DEST(dest) ((eim_mode) ? dest : dest << 8)
 
+static int __read_mostly eim_mode;
 static struct ioapic_scope ir_ioapic[MAX_IO_APICS];
 static struct hpet_scope ir_hpet[MAX_HPET_TBS];
 
@@ -481,11 +482,11 @@ static int intel_setup_irq_remapping(struct intel_iommu *iommu)
        if (iommu->ir_table)
                return 0;
 
-       ir_table = kzalloc(sizeof(struct ir_table), GFP_ATOMIC);
+       ir_table = kzalloc(sizeof(struct ir_table), GFP_KERNEL);
        if (!ir_table)
                return -ENOMEM;
 
-       pages = alloc_pages_node(iommu->node, GFP_ATOMIC | __GFP_ZERO,
+       pages = alloc_pages_node(iommu->node, GFP_KERNEL | __GFP_ZERO,
                                 INTR_REMAP_PAGE_ORDER);
 
        if (!pages) {
@@ -566,13 +567,27 @@ static int __init dmar_x2apic_optout(void)
        return dmar->flags & DMAR_X2APIC_OPT_OUT;
 }
 
-static int __init intel_irq_remapping_supported(void)
+static void __init intel_cleanup_irq_remapping(void)
+{
+       struct dmar_drhd_unit *drhd;
+       struct intel_iommu *iommu;
+
+       for_each_iommu(iommu, drhd) {
+               if (ecap_ir_support(iommu->ecap)) {
+                       iommu_disable_irq_remapping(iommu);
+                       intel_teardown_irq_remapping(iommu);
+               }
+       }
+
+       if (x2apic_supported())
+               pr_warn("Failed to enable irq remapping.  You are vulnerable to irq-injection attacks.\n");
+}
+
+static int __init intel_prepare_irq_remapping(void)
 {
        struct dmar_drhd_unit *drhd;
        struct intel_iommu *iommu;
 
-       if (disable_irq_remap)
-               return 0;
        if (irq_remap_broken) {
                printk(KERN_WARNING
                        "This system BIOS has enabled interrupt remapping\n"
@@ -581,38 +596,45 @@ static int __init intel_irq_remapping_supported(void)
                        "interrupt remapping is being disabled.  Please\n"
                        "contact your BIOS vendor for an update\n");
                add_taint(TAINT_FIRMWARE_WORKAROUND, LOCKDEP_STILL_OK);
-               disable_irq_remap = 1;
-               return 0;
+               return -ENODEV;
        }
 
+       if (dmar_table_init() < 0)
+               return -ENODEV;
+
        if (!dmar_ir_support())
-               return 0;
+               return -ENODEV;
+
+       if (parse_ioapics_under_ir() != 1) {
+               printk(KERN_INFO "Not enabling interrupt remapping\n");
+               goto error;
+       }
 
+       /* First make sure all IOMMUs support IRQ remapping */
        for_each_iommu(iommu, drhd)
                if (!ecap_ir_support(iommu->ecap))
-                       return 0;
+                       goto error;
 
-       return 1;
+       /* Do the allocations early */
+       for_each_iommu(iommu, drhd)
+               if (intel_setup_irq_remapping(iommu))
+                       goto error;
+
+       return 0;
+
+error:
+       intel_cleanup_irq_remapping();
+       return -ENODEV;
 }
 
 static int __init intel_enable_irq_remapping(void)
 {
        struct dmar_drhd_unit *drhd;
        struct intel_iommu *iommu;
-       bool x2apic_present;
-       int setup = 0;
+       bool setup = false;
        int eim = 0;
 
-       x2apic_present = x2apic_supported();
-
-       if (parse_ioapics_under_ir() != 1) {
-               printk(KERN_INFO "Not enable interrupt remapping\n");
-               goto error;
-       }
-
-       if (x2apic_present) {
-               pr_info("Queued invalidation will be enabled to support x2apic and Intr-remapping.\n");
-
+       if (x2apic_supported()) {
                eim = !dmar_x2apic_optout();
                if (!eim)
                        pr_info("x2apic is disabled because BIOS sets x2apic opt out bit. You can use 'intremap=no_x2apic_optout' to override the BIOS setting.\n");
@@ -643,16 +665,15 @@ static int __init intel_enable_irq_remapping(void)
        /*
         * check for the Interrupt-remapping support
         */
-       for_each_iommu(iommu, drhd) {
-               if (!ecap_ir_support(iommu->ecap))
-                       continue;
-
+       for_each_iommu(iommu, drhd)
                if (eim && !ecap_eim_support(iommu->ecap)) {
                        printk(KERN_INFO "DRHD %Lx: EIM not supported by DRHD, "
                               " ecap %Lx\n", drhd->reg_base_addr, iommu->ecap);
-                       goto error;
+                       eim = 0;
                }
-       }
+       eim_mode = eim;
+       if (eim)
+               pr_info("Queued invalidation will be enabled to support x2apic and Intr-remapping.\n");
 
        /*
         * Enable queued invalidation for all the DRHD's.
@@ -672,14 +693,8 @@ static int __init intel_enable_irq_remapping(void)
         * Setup Interrupt-remapping for all the DRHD's now.
         */
        for_each_iommu(iommu, drhd) {
-               if (!ecap_ir_support(iommu->ecap))
-                       continue;
-
-               if (intel_setup_irq_remapping(iommu))
-                       goto error;
-
                iommu_set_irq_remapping(iommu, eim);
-               setup = 1;
+               setup = true;
        }
 
        if (!setup)
@@ -699,15 +714,7 @@ static int __init intel_enable_irq_remapping(void)
        return eim ? IRQ_REMAP_X2APIC_MODE : IRQ_REMAP_XAPIC_MODE;
 
 error:
-       for_each_iommu(iommu, drhd)
-               if (ecap_ir_support(iommu->ecap)) {
-                       iommu_disable_irq_remapping(iommu);
-                       intel_teardown_irq_remapping(iommu);
-               }
-
-       if (x2apic_present)
-               pr_warn("Failed to enable irq remapping.  You are vulnerable to irq-injection attacks.\n");
-
+       intel_cleanup_irq_remapping();
        return -1;
 }
 
@@ -846,7 +853,7 @@ static int __init parse_ioapics_under_ir(void)
 {
        struct dmar_drhd_unit *drhd;
        struct intel_iommu *iommu;
-       int ir_supported = 0;
+       bool ir_supported = false;
        int ioapic_idx;
 
        for_each_iommu(iommu, drhd)
@@ -854,7 +861,7 @@ static int __init parse_ioapics_under_ir(void)
                        if (ir_parse_ioapic_hpet_scope(drhd->hdr, iommu))
                                return -1;
 
-                       ir_supported = 1;
+                       ir_supported = true;
                }
 
        if (!ir_supported)
@@ -907,7 +914,7 @@ static void disable_irq_remapping(void)
 static int reenable_irq_remapping(int eim)
 {
        struct dmar_drhd_unit *drhd;
-       int setup = 0;
+       bool setup = false;
        struct intel_iommu *iommu = NULL;
 
        for_each_iommu(iommu, drhd)
@@ -923,7 +930,7 @@ static int reenable_irq_remapping(int eim)
 
                /* Set up interrupt remapping for iommu.*/
                iommu_set_irq_remapping(iommu, eim);
-               setup = 1;
+               setup = true;
        }
 
        if (!setup)
@@ -1196,8 +1203,7 @@ static int intel_alloc_hpet_msi(unsigned int irq, unsigned int id)
 }
 
 struct irq_remap_ops intel_irq_remap_ops = {
-       .supported              = intel_irq_remapping_supported,
-       .prepare                = dmar_table_init,
+       .prepare                = intel_prepare_irq_remapping,
        .enable                 = intel_enable_irq_remapping,
        .disable                = disable_irq_remapping,
        .reenable               = reenable_irq_remapping,