iommu/vt-d: Differentiate relaxable and non relaxable RMRRs
[linux-2.6-microblaze.git] / drivers / iommu / intel-iommu.c
index 876096c..10bdf7e 100644 (file)
@@ -324,7 +324,6 @@ struct dmar_rmrr_unit {
        u64     end_address;            /* reserved end address */
        struct dmar_dev_scope *devices; /* target devices */
        int     devices_cnt;            /* target device count */
-       struct iommu_resv_region *resv; /* reserved region handle */
 };
 
 struct dmar_atsr_unit {
@@ -730,12 +729,39 @@ static int iommu_dummy(struct device *dev)
        return dev->archdata.iommu == DUMMY_DEVICE_DOMAIN_INFO;
 }
 
+/**
+ * is_downstream_to_pci_bridge - test if a device belongs to the PCI
+ *                              sub-hierarchy of a candidate PCI-PCI bridge
+ * @dev: candidate PCI device belonging to @bridge PCI sub-hierarchy
+ * @bridge: the candidate PCI-PCI bridge
+ *
+ * Return: true if @dev belongs to @bridge PCI sub-hierarchy, else false.
+ */
+static bool
+is_downstream_to_pci_bridge(struct device *dev, struct device *bridge)
+{
+       struct pci_dev *pdev, *pbridge;
+
+       if (!dev_is_pci(dev) || !dev_is_pci(bridge))
+               return false;
+
+       pdev = to_pci_dev(dev);
+       pbridge = to_pci_dev(bridge);
+
+       if (pbridge->subordinate &&
+           pbridge->subordinate->number <= pdev->bus->number &&
+           pbridge->subordinate->busn_res.end >= pdev->bus->number)
+               return true;
+
+       return false;
+}
+
 static struct intel_iommu *device_to_iommu(struct device *dev, u8 *bus, u8 *devfn)
 {
        struct dmar_drhd_unit *drhd = NULL;
        struct intel_iommu *iommu;
        struct device *tmp;
-       struct pci_dev *ptmp, *pdev = NULL;
+       struct pci_dev *pdev = NULL;
        u16 segment = 0;
        int i;
 
@@ -781,13 +807,7 @@ static struct intel_iommu *device_to_iommu(struct device *dev, u8 *bus, u8 *devf
                                goto out;
                        }
 
-                       if (!pdev || !dev_is_pci(tmp))
-                               continue;
-
-                       ptmp = to_pci_dev(tmp);
-                       if (ptmp->subordinate &&
-                           ptmp->subordinate->number <= pdev->bus->number &&
-                           ptmp->subordinate->busn_res.end >= pdev->bus->number)
+                       if (is_downstream_to_pci_bridge(dev, tmp))
                                goto got_pdev;
                }
 
@@ -2860,7 +2880,8 @@ static bool device_has_rmrr(struct device *dev)
                 */
                for_each_active_dev_scope(rmrr->devices,
                                          rmrr->devices_cnt, i, tmp)
-                       if (tmp == dev) {
+                       if (tmp == dev ||
+                           is_downstream_to_pci_bridge(dev, tmp)) {
                                rcu_read_unlock();
                                return true;
                        }
@@ -2869,6 +2890,35 @@ static bool device_has_rmrr(struct device *dev)
        return false;
 }
 
+/**
+ * device_rmrr_is_relaxable - Test whether the RMRR of this device
+ * is relaxable (ie. is allowed to be not enforced under some conditions)
+ * @dev: device handle
+ *
+ * We assume that PCI USB devices with RMRRs have them largely
+ * for historical reasons and that the RMRR space is not actively used post
+ * boot.  This exclusion may change if vendors begin to abuse it.
+ *
+ * The same exception is made for graphics devices, with the requirement that
+ * any use of the RMRR regions will be torn down before assigning the device
+ * to a guest.
+ *
+ * Return: true if the RMRR is relaxable, false otherwise
+ */
+static bool device_rmrr_is_relaxable(struct device *dev)
+{
+       struct pci_dev *pdev;
+
+       if (!dev_is_pci(dev))
+               return false;
+
+       pdev = to_pci_dev(dev);
+       if (IS_USB_DEVICE(pdev) || IS_GFX_DEVICE(pdev))
+               return true;
+       else
+               return false;
+}
+
 /*
  * There are a couple cases where we need to restrict the functionality of
  * devices associated with RMRRs.  The first is when evaluating a device for
@@ -2883,25 +2933,16 @@ static bool device_has_rmrr(struct device *dev)
  * We therefore prevent devices associated with an RMRR from participating in
  * the IOMMU API, which eliminates them from device assignment.
  *
- * In both cases we assume that PCI USB devices with RMRRs have them largely
- * for historical reasons and that the RMRR space is not actively used post
- * boot.  This exclusion may change if vendors begin to abuse it.
- *
- * The same exception is made for graphics devices, with the requirement that
- * any use of the RMRR regions will be torn down before assigning the device
- * to a guest.
+ * In both cases, devices which have relaxable RMRRs are not concerned by this
+ * restriction. See device_rmrr_is_relaxable comment.
  */
 static bool device_is_rmrr_locked(struct device *dev)
 {
        if (!device_has_rmrr(dev))
                return false;
 
-       if (dev_is_pci(dev)) {
-               struct pci_dev *pdev = to_pci_dev(dev);
-
-               if (IS_USB_DEVICE(pdev) || IS_GFX_DEVICE(pdev))
-                       return false;
-       }
+       if (device_rmrr_is_relaxable(dev))
+               return false;
 
        return true;
 }
@@ -4050,7 +4091,6 @@ static inline void init_iommu_pm_ops(void) {}
 int __init dmar_parse_one_rmrr(struct acpi_dmar_header *header, void *arg)
 {
        struct acpi_dmar_reserved_memory *rmrr;
-       int prot = DMA_PTE_READ|DMA_PTE_WRITE;
        struct dmar_rmrr_unit *rmrru;
        size_t length;
 
@@ -4064,22 +4104,16 @@ int __init dmar_parse_one_rmrr(struct acpi_dmar_header *header, void *arg)
        rmrru->end_address = rmrr->end_address;
 
        length = rmrr->end_address - rmrr->base_address + 1;
-       rmrru->resv = iommu_alloc_resv_region(rmrr->base_address, length, prot,
-                                             IOMMU_RESV_DIRECT);
-       if (!rmrru->resv)
-               goto free_rmrru;
 
        rmrru->devices = dmar_alloc_dev_scope((void *)(rmrr + 1),
                                ((void *)rmrr) + rmrr->header.length,
                                &rmrru->devices_cnt);
        if (rmrru->devices_cnt && rmrru->devices == NULL)
-               goto free_all;
+               goto free_rmrru;
 
        list_add(&rmrru->list, &dmar_rmrr_units);
 
        return 0;
-free_all:
-       kfree(rmrru->resv);
 free_rmrru:
        kfree(rmrru);
 out:
@@ -4297,7 +4331,6 @@ static void intel_iommu_free_dmars(void)
        list_for_each_entry_safe(rmrru, rmrr_n, &dmar_rmrr_units, list) {
                list_del(&rmrru->list);
                dmar_free_dev_scope(&rmrru->devices, &rmrru->devices_cnt);
-               kfree(rmrru->resv);
                kfree(rmrru);
        }
 
@@ -5400,22 +5433,38 @@ static void intel_iommu_remove_device(struct device *dev)
 static void intel_iommu_get_resv_regions(struct device *device,
                                         struct list_head *head)
 {
+       int prot = DMA_PTE_READ | DMA_PTE_WRITE;
        struct iommu_resv_region *reg;
        struct dmar_rmrr_unit *rmrr;
        struct device *i_dev;
        int i;
 
-       rcu_read_lock();
+       down_read(&dmar_global_lock);
        for_each_rmrr_units(rmrr) {
                for_each_active_dev_scope(rmrr->devices, rmrr->devices_cnt,
                                          i, i_dev) {
-                       if (i_dev != device)
+                       struct iommu_resv_region *resv;
+                       enum iommu_resv_type type;
+                       size_t length;
+
+                       if (i_dev != device &&
+                           !is_downstream_to_pci_bridge(device, i_dev))
                                continue;
 
-                       list_add_tail(&rmrr->resv->list, head);
+                       length = rmrr->end_address - rmrr->base_address + 1;
+
+                       type = device_rmrr_is_relaxable(device) ?
+                               IOMMU_RESV_DIRECT_RELAXABLE : IOMMU_RESV_DIRECT;
+
+                       resv = iommu_alloc_resv_region(rmrr->base_address,
+                                                      length, prot, type);
+                       if (!resv)
+                               break;
+
+                       list_add_tail(&resv->list, head);
                }
        }
-       rcu_read_unlock();
+       up_read(&dmar_global_lock);
 
 #ifdef CONFIG_INTEL_IOMMU_FLOPPY_WA
        if (dev_is_pci(device)) {
@@ -5443,10 +5492,8 @@ static void intel_iommu_put_resv_regions(struct device *dev,
 {
        struct iommu_resv_region *entry, *next;
 
-       list_for_each_entry_safe(entry, next, head, list) {
-               if (entry->type == IOMMU_RESV_MSI)
-                       kfree(entry);
-       }
+       list_for_each_entry_safe(entry, next, head, list)
+               kfree(entry);
 }
 
 int intel_iommu_enable_pasid(struct intel_iommu *iommu, struct device *dev)