summaryrefslogtreecommitdiffstats
path: root/drivers/pci
diff options
context:
space:
mode:
authorJacek Lawrynowicz <jacek.lawrynowicz@intel.com>2016-03-03 15:38:02 +0100
committerBjorn Helgaas <bhelgaas@google.com>2016-04-11 21:34:32 +0200
commit338c3149a221527e202ee26b1e35f76c965bb6c0 (patch)
treea5dfc811d70258c5abac1422e7bd4484a550c165 /drivers/pci
parentPCI: Move informational printk to pci_add_dma_alias() (diff)
downloadlinux-338c3149a221527e202ee26b1e35f76c965bb6c0.tar.xz
linux-338c3149a221527e202ee26b1e35f76c965bb6c0.zip
PCI: Add support for multiple DMA aliases
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>
Diffstat (limited to 'drivers/pci')
-rw-r--r--drivers/pci/pci.c19
-rw-r--r--drivers/pci/probe.c1
-rw-r--r--drivers/pci/search.c14
3 files changed, 27 insertions, 7 deletions
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index c82ebd0f6982..0b90c2186f1c 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -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;
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index 8004f67c57ec..ae7daeb83e21 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -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);
}
diff --git a/drivers/pci/search.c b/drivers/pci/search.c
index a20ce7d5e2a7..33e0f033a48e 100644
--- a/drivers/pci/search.c
+++ b/drivers/pci/search.c
@@ -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) {