PCI: Add support for multiple DMA aliases
authorJacek Lawrynowicz <jacek.lawrynowicz@intel.com>
Thu, 3 Mar 2016 14:38:02 +0000 (15:38 +0100)
committerBjorn Helgaas <bhelgaas@google.com>
Mon, 11 Apr 2016 19:34:32 +0000 (14:34 -0500)
Solve IOMMU support issues with PCIe non-transparent bridges that use
Requester ID look-up tables (RID-LUT), e.g., the PEX8733.

The NTB connects devices in two independent PCI domains.  Devices separated
by the NTB are not able to discover each other.  A PCI packet being
forwared from one domain to another has to have its RID modified so it
appears on correct bus and completions are forwarded back to the original
domain through the NTB.  The RID is translated using a preprogrammed table
(LUT) and the PCI packet propagates upstream away from the NTB.  If the
destination system has IOMMU enabled, the packet will be discarded because
the new RID is unknown to the IOMMU.  Adding a DMA alias for the new RID
allows IOMMU to properly recognize the packet.

Each device behind the NTB has a unique RID assigned in the RID-LUT.  The
current DMA alias implementation supports only a single alias, so it's not
possible to support mutiple devices behind the NTB when IOMMU is enabled.

Enable all possible aliases on a given bus (256) that are stored in a
bitset.  Alias devfn is directly translated to a bit number.  The bitset is
not allocated for devices that have no need for DMA aliases.

More details can be found in the following article:
http://www.plxtech.com/files/pdf/technical/expresslane/RTC_Enabling%20MulitHostSystemDesigns.pdf

Signed-off-by: Jacek Lawrynowicz <jacek.lawrynowicz@intel.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Reviewed-by: Alex Williamson <alex.williamson@redhat.com>
Acked-by: David Woodhouse <David.Woodhouse@intel.com>
Acked-by: Joerg Roedel <jroedel@suse.de>
drivers/iommu/iommu.c
drivers/pci/pci.c
drivers/pci/probe.c
drivers/pci/search.c
include/linux/pci.h

index bfd4f7c3b1d8a9da9ce6cd2bce3decd8e27e0fca..1b49e940a31888dacf509f0137ae5a5c10cd11d5 100644 (file)
@@ -660,8 +660,8 @@ static struct iommu_group *get_pci_function_alias_group(struct pci_dev *pdev,
 }
 
 /*
- * Look for aliases to or from the given device for exisiting groups.  The
- * dma_alias_devfn only supports aliases on the same bus, therefore the search
+ * Look for aliases to or from the given device for existing groups. DMA
+ * aliases are only supported on the same bus, therefore the search
  * space is quite small (especially since we're really only looking at pcie
  * device, and therefore only expect multiple slots on the root complex or
  * downstream switch ports).  It's conceivable though that a pair of
@@ -686,11 +686,7 @@ static struct iommu_group *get_pci_alias_group(struct pci_dev *pdev,
                        continue;
 
                /* We alias them or they alias us */
-               if (((pdev->dev_flags & PCI_DEV_FLAGS_DMA_ALIAS_DEVFN) &&
-                    pdev->dma_alias_devfn == tmp->devfn) ||
-                   ((tmp->dev_flags & PCI_DEV_FLAGS_DMA_ALIAS_DEVFN) &&
-                    tmp->dma_alias_devfn == pdev->devfn)) {
-
+               if (pci_devs_are_dma_aliases(pdev, tmp)) {
                        group = get_pci_alias_group(tmp, devfns);
                        if (group) {
                                pci_dev_put(tmp);
index c82ebd0f698271abc85206c73c095ba07d89e778..0b90c2186f1c7476d031f02bbcb178280f331715 100644 (file)
@@ -4588,12 +4588,27 @@ int pci_set_vga_state(struct pci_dev *dev, bool decode,
  */
 void pci_add_dma_alias(struct pci_dev *dev, u8 devfn)
 {
-       dev->dma_alias_devfn = devfn;
-       dev->dev_flags |= PCI_DEV_FLAGS_DMA_ALIAS_DEVFN;
+       if (!dev->dma_alias_mask)
+               dev->dma_alias_mask = kcalloc(BITS_TO_LONGS(U8_MAX),
+                                             sizeof(long), GFP_KERNEL);
+       if (!dev->dma_alias_mask) {
+               dev_warn(&dev->dev, "Unable to allocate DMA alias mask\n");
+               return;
+       }
+
+       set_bit(devfn, dev->dma_alias_mask);
        dev_info(&dev->dev, "Enabling fixed DMA alias to %02x.%d\n",
                 PCI_SLOT(devfn), PCI_FUNC(devfn));
 }
 
+bool pci_devs_are_dma_aliases(struct pci_dev *dev1, struct pci_dev *dev2)
+{
+       return (dev1->dma_alias_mask &&
+               test_bit(dev2->devfn, dev1->dma_alias_mask)) ||
+              (dev2->dma_alias_mask &&
+               test_bit(dev1->devfn, dev2->dma_alias_mask));
+}
+
 bool pci_device_is_present(struct pci_dev *pdev)
 {
        u32 v;
index 8004f67c57ec6ae59157714e507f06429127765e..ae7daeb83e21087422e1a34d2da715e86276fa0f 100644 (file)
@@ -1537,6 +1537,7 @@ static void pci_release_dev(struct device *dev)
        pcibios_release_device(pci_dev);
        pci_bus_put(pci_dev->bus);
        kfree(pci_dev->driver_override);
+       kfree(pci_dev->dma_alias_mask);
        kfree(pci_dev);
 }
 
index a20ce7d5e2a718551554a1e860457b852756d97b..33e0f033a48e7e2f6097b13d4425d8d17b1cabb7 100644 (file)
@@ -40,11 +40,15 @@ int pci_for_each_dma_alias(struct pci_dev *pdev,
         * If the device is broken and uses an alias requester ID for
         * DMA, iterate over that too.
         */
-       if (unlikely(pdev->dev_flags & PCI_DEV_FLAGS_DMA_ALIAS_DEVFN)) {
-               ret = fn(pdev, PCI_DEVID(pdev->bus->number,
-                                        pdev->dma_alias_devfn), data);
-               if (ret)
-                       return ret;
+       if (unlikely(pdev->dma_alias_mask)) {
+               u8 devfn;
+
+               for_each_set_bit(devfn, pdev->dma_alias_mask, U8_MAX) {
+                       ret = fn(pdev, PCI_DEVID(pdev->bus->number, devfn),
+                                data);
+                       if (ret)
+                               return ret;
+               }
        }
 
        for (bus = pdev->bus; !pci_is_root_bus(bus); bus = bus->parent) {
index 7e7019064437c6868cb9d303190a464e9bb4abe4..5581d05f7833b3a33b8e21c1dd18651567efd79a 100644 (file)
@@ -166,8 +166,6 @@ enum pci_dev_flags {
        PCI_DEV_FLAGS_ASSIGNED = (__force pci_dev_flags_t) (1 << 2),
        /* Flag for quirk use to store if quirk-specific ACS is enabled */
        PCI_DEV_FLAGS_ACS_ENABLED_QUIRK = (__force pci_dev_flags_t) (1 << 3),
-       /* Flag to indicate the device uses dma_alias_devfn */
-       PCI_DEV_FLAGS_DMA_ALIAS_DEVFN = (__force pci_dev_flags_t) (1 << 4),
        /* Use a PCIe-to-PCI bridge alias even if !pci_is_pcie */
        PCI_DEV_FLAG_PCIE_BRIDGE_ALIAS = (__force pci_dev_flags_t) (1 << 5),
        /* Do not use bus resets for device */
@@ -273,7 +271,7 @@ struct pci_dev {
        u8              rom_base_reg;   /* which config register controls the ROM */
        u8              pin;            /* which interrupt pin this device uses */
        u16             pcie_flags_reg; /* cached PCIe Capabilities Register */
-       u8              dma_alias_devfn;/* devfn of DMA alias, if any */
+       unsigned long   *dma_alias_mask;/* mask of enabled devfn aliases */
 
        struct pci_driver *driver;      /* which driver has allocated this device */
        u64             dma_mask;       /* Mask of the bits of bus address this
@@ -1989,6 +1987,7 @@ static inline struct eeh_dev *pci_dev_to_eeh_dev(struct pci_dev *pdev)
 #endif
 
 void pci_add_dma_alias(struct pci_dev *dev, u8 devfn);
+bool pci_devs_are_dma_aliases(struct pci_dev *dev1, struct pci_dev *dev2);
 int pci_for_each_dma_alias(struct pci_dev *pdev,
                           int (*fn)(struct pci_dev *pdev,
                                     u16 alias, void *data), void *data);