powerpc/eeh: Fetch IOMMU table in reliable way
authorGavin Shan <gwshan@linux.vnet.ibm.com>
Tue, 15 Jul 2014 07:00:56 +0000 (17:00 +1000)
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>
Tue, 5 Aug 2014 05:41:16 +0000 (15:41 +1000)
Function eeh_iommu_group_to_pe() iterates each PCI device to check
the binding IOMMU group with get_iommu_table_base(), which possibly
fetches pdev->dev.archdata.dma_data.dma_offset. It's (0x1 << 59)
for "bypass" cases.

The patch fixes the issue by iterating devices hooked to the IOMMU
group and fetch IOMMU table there.

Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
arch/powerpc/kernel/eeh.c

index 18c40fd1e62af9c3cb855852295bf2981e9c7e3a..4de2103a30c791542b3eb9a84d4f12b3847a4150 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/init.h>
 #include <linux/list.h>
 #include <linux/pci.h>
+#include <linux/iommu.h>
 #include <linux/proc_fs.h>
 #include <linux/rbtree.h>
 #include <linux/reboot.h>
@@ -1178,6 +1179,24 @@ out:
 }
 EXPORT_SYMBOL(eeh_dev_release);
 
+static int dev_has_iommu_table(struct device *dev, void *data)
+{
+       struct pci_dev *pdev = to_pci_dev(dev);
+       struct pci_dev **ppdev = data;
+       struct iommu_table *tbl;
+
+       if (!dev)
+               return 0;
+
+       tbl = get_iommu_table_base(dev);
+       if (tbl && tbl->it_group) {
+               *ppdev = pdev;
+               return 1;
+       }
+
+       return 0;
+}
+
 /**
  * eeh_iommu_group_to_pe - Convert IOMMU group to EEH PE
  * @group: IOMMU group
@@ -1186,24 +1205,16 @@ EXPORT_SYMBOL(eeh_dev_release);
  */
 struct eeh_pe *eeh_iommu_group_to_pe(struct iommu_group *group)
 {
-       struct iommu_table *tbl;
        struct pci_dev *pdev = NULL;
        struct eeh_dev *edev;
-       bool found = false;
+       int ret;
 
        /* No IOMMU group ? */
        if (!group)
                return NULL;
 
-       /* No PCI device ? */
-       for_each_pci_dev(pdev) {
-               tbl = get_iommu_table_base(&pdev->dev);
-               if (tbl && tbl->it_group == group) {
-                       found = true;
-                       break;
-               }
-       }
-       if (!found)
+       ret = iommu_group_for_each_dev(group, &pdev, dev_has_iommu_table);
+       if (!ret || !pdev)
                return NULL;
 
        /* No EEH device or PE ? */