iommu/vt-d: Differentiate relaxable and non relaxable RMRRs
[linux-2.6-microblaze.git] / drivers / iommu / intel-iommu.c
index 082fbb1..10bdf7e 100644 (file)
@@ -729,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;
 
@@ -780,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;
                }
 
@@ -2859,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;
                        }
@@ -2868,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
@@ -2882,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;
 }
@@ -5402,15 +5444,20 @@ static void intel_iommu_get_resv_regions(struct device *device,
                for_each_active_dev_scope(rmrr->devices, rmrr->devices_cnt,
                                          i, i_dev) {
                        struct iommu_resv_region *resv;
+                       enum iommu_resv_type type;
                        size_t length;
 
-                       if (i_dev != device)
+                       if (i_dev != device &&
+                           !is_downstream_to_pci_bridge(device, i_dev))
                                continue;
 
                        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,
-                                                      IOMMU_RESV_DIRECT);
+                                                      length, prot, type);
                        if (!resv)
                                break;