Merge git://git.infradead.org/users/dwmw2/iommu-2.6.32
authorLinus Torvalds <torvalds@linux-foundation.org>
Sat, 14 Nov 2009 21:05:27 +0000 (13:05 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Sat, 14 Nov 2009 21:05:27 +0000 (13:05 -0800)
* git://git.infradead.org/users/dwmw2/iommu-2.6.32:
  intel-iommu: Support PCIe hot-plug
  intel-iommu: Obey coherent_dma_mask for alloc_coherent on passthrough
  intel-iommu: Check for 'DMAR at zero' BIOS error earlier.

drivers/pci/dmar.c
drivers/pci/intel-iommu.c

index 22b02c6df8542138a27fcad4511eb008f0afbb0c..e5f8fc164fd3329218d6d15a43ba76072c0079b3 100644 (file)
@@ -175,15 +175,6 @@ dmar_parse_one_drhd(struct acpi_dmar_header *header)
        int ret = 0;
 
        drhd = (struct acpi_dmar_hardware_unit *)header;
-       if (!drhd->address) {
-               /* Promote an attitude of violence to a BIOS engineer today */
-               WARN(1, "Your BIOS is broken; DMAR reported at address zero!\n"
-                    "BIOS vendor: %s; Ver: %s; Product Version: %s\n",
-                    dmi_get_system_info(DMI_BIOS_VENDOR),
-                    dmi_get_system_info(DMI_BIOS_VERSION),
-                    dmi_get_system_info(DMI_PRODUCT_VERSION));
-               return -ENODEV;
-       }
        dmaru = kzalloc(sizeof(*dmaru), GFP_KERNEL);
        if (!dmaru)
                return -ENOMEM;
@@ -591,12 +582,50 @@ int __init dmar_table_init(void)
        return 0;
 }
 
+int __init check_zero_address(void)
+{
+       struct acpi_table_dmar *dmar;
+       struct acpi_dmar_header *entry_header;
+       struct acpi_dmar_hardware_unit *drhd;
+
+       dmar = (struct acpi_table_dmar *)dmar_tbl;
+       entry_header = (struct acpi_dmar_header *)(dmar + 1);
+
+       while (((unsigned long)entry_header) <
+                       (((unsigned long)dmar) + dmar_tbl->length)) {
+               /* Avoid looping forever on bad ACPI tables */
+               if (entry_header->length == 0) {
+                       printk(KERN_WARNING PREFIX
+                               "Invalid 0-length structure\n");
+                       return 0;
+               }
+
+               if (entry_header->type == ACPI_DMAR_TYPE_HARDWARE_UNIT) {
+                       drhd = (void *)entry_header;
+                       if (!drhd->address) {
+                               /* Promote an attitude of violence to a BIOS engineer today */
+                               WARN(1, "Your BIOS is broken; DMAR reported at address zero!\n"
+                                    "BIOS vendor: %s; Ver: %s; Product Version: %s\n",
+                                    dmi_get_system_info(DMI_BIOS_VENDOR),
+                                    dmi_get_system_info(DMI_BIOS_VERSION),
+                                    dmi_get_system_info(DMI_PRODUCT_VERSION));
+                               return 0;
+                       }
+                       break;
+               }
+
+               entry_header = ((void *)entry_header + entry_header->length);
+       }
+       return 1;
+}
+
 void __init detect_intel_iommu(void)
 {
        int ret;
 
        ret = dmar_table_detect();
-
+       if (ret)
+               ret = check_zero_address();
        {
 #ifdef CONFIG_INTR_REMAP
                struct acpi_table_dmar *dmar;
index b1e97e6825009295088d62b183f8136616bbe917..1840a0578a429366874ca049d05af748a1f88c09 100644 (file)
@@ -2767,7 +2767,15 @@ static void *intel_alloc_coherent(struct device *hwdev, size_t size,
 
        size = PAGE_ALIGN(size);
        order = get_order(size);
-       flags &= ~(GFP_DMA | GFP_DMA32);
+
+       if (!iommu_no_mapping(hwdev))
+               flags &= ~(GFP_DMA | GFP_DMA32);
+       else if (hwdev->coherent_dma_mask < dma_get_required_mask(hwdev)) {
+               if (hwdev->coherent_dma_mask < DMA_BIT_MASK(32))
+                       flags |= GFP_DMA;
+               else
+                       flags |= GFP_DMA32;
+       }
 
        vaddr = (void *)__get_free_pages(flags, order);
        if (!vaddr)
@@ -3207,6 +3215,33 @@ static int __init init_iommu_sysfs(void)
 }
 #endif /* CONFIG_PM */
 
+/*
+ * Here we only respond to action of unbound device from driver.
+ *
+ * Added device is not attached to its DMAR domain here yet. That will happen
+ * when mapping the device to iova.
+ */
+static int device_notifier(struct notifier_block *nb,
+                                 unsigned long action, void *data)
+{
+       struct device *dev = data;
+       struct pci_dev *pdev = to_pci_dev(dev);
+       struct dmar_domain *domain;
+
+       domain = find_domain(pdev);
+       if (!domain)
+               return 0;
+
+       if (action == BUS_NOTIFY_UNBOUND_DRIVER && !iommu_pass_through)
+               domain_remove_one_dev_info(domain, pdev);
+
+       return 0;
+}
+
+static struct notifier_block device_nb = {
+       .notifier_call = device_notifier,
+};
+
 int __init intel_iommu_init(void)
 {
        int ret = 0;
@@ -3259,6 +3294,8 @@ int __init intel_iommu_init(void)
 
        register_iommu(&intel_iommu_ops);
 
+       bus_register_notifier(&pci_bus_type, &device_nb);
+
        return 0;
 }