iommu/{vt-d,amd}: Remove multifunction assumption around grouping
authorAlex Williamson <alex.williamson@redhat.com>
Thu, 30 May 2013 18:39:18 +0000 (12:39 -0600)
committerJoerg Roedel <joro@8bytes.org>
Thu, 20 Jun 2013 15:21:09 +0000 (17:21 +0200)
If a device is multifunction and does not have ACS enabled then we
assume that the entire package lacks ACS and use function 0 as the
base of the group.  The PCIe spec however states that components are
permitted to implement ACS on some, none, or all of their applicable
functions.  It's therefore conceivable that function 0 may be fully
independent and support ACS while other functions do not.  Instead
use the lowest function of the slot that does not have ACS enabled
as the base of the group.  This may be the current device, which is
intentional.  So long as we use a consistent algorithm, all the
non-ACS functions will be grouped together and ACS functions will
get separate groups.

Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
Signed-off-by: Joerg Roedel <joro@8bytes.org>
drivers/iommu/amd_iommu.c
drivers/iommu/intel-iommu.c

index 21d02b0d907c1ada17aa3b1d0e13352cd2e4cbf3..5a02d0776a66a9a21f57bcd2867cae613a60eb2e 100644 (file)
@@ -287,14 +287,27 @@ static struct pci_dev *get_isolation_root(struct pci_dev *pdev)
 
        /*
         * If it's a multifunction device that does not support our
-        * required ACS flags, add to the same group as function 0.
+        * required ACS flags, add to the same group as lowest numbered
+        * function that also does not suport the required ACS flags.
         */
        if (dma_pdev->multifunction &&
-           !pci_acs_enabled(dma_pdev, REQ_ACS_FLAGS))
-               swap_pci_ref(&dma_pdev,
-                            pci_get_slot(dma_pdev->bus,
-                                         PCI_DEVFN(PCI_SLOT(dma_pdev->devfn),
-                                         0)));
+           !pci_acs_enabled(dma_pdev, REQ_ACS_FLAGS)) {
+               u8 i, slot = PCI_SLOT(dma_pdev->devfn);
+
+               for (i = 0; i < 8; i++) {
+                       struct pci_dev *tmp;
+
+                       tmp = pci_get_slot(dma_pdev->bus, PCI_DEVFN(slot, i));
+                       if (!tmp)
+                               continue;
+
+                       if (!pci_acs_enabled(tmp, REQ_ACS_FLAGS)) {
+                               swap_pci_ref(&dma_pdev, tmp);
+                               break;
+                       }
+                       pci_dev_put(tmp);
+               }
+       }
 
        /*
         * Devices on the root bus go through the iommu.  If that's not us,
index b4f0e28dfa41d6bbff0314783360d455dd6c45ef..eec0d3e04bf578ab6a6afe36e1c55ad5d4b0c969 100644 (file)
@@ -4182,14 +4182,27 @@ static int intel_iommu_add_device(struct device *dev)
 
        /*
         * If it's a multifunction device that does not support our
-        * required ACS flags, add to the same group as function 0.
+        * required ACS flags, add to the same group as lowest numbered
+        * function that also does not suport the required ACS flags.
         */
        if (dma_pdev->multifunction &&
-           !pci_acs_enabled(dma_pdev, REQ_ACS_FLAGS))
-               swap_pci_ref(&dma_pdev,
-                            pci_get_slot(dma_pdev->bus,
-                                         PCI_DEVFN(PCI_SLOT(dma_pdev->devfn),
-                                         0)));
+           !pci_acs_enabled(dma_pdev, REQ_ACS_FLAGS)) {
+               u8 i, slot = PCI_SLOT(dma_pdev->devfn);
+
+               for (i = 0; i < 8; i++) {
+                       struct pci_dev *tmp;
+
+                       tmp = pci_get_slot(dma_pdev->bus, PCI_DEVFN(slot, i));
+                       if (!tmp)
+                               continue;
+
+                       if (!pci_acs_enabled(tmp, REQ_ACS_FLAGS)) {
+                               swap_pci_ref(&dma_pdev, tmp);
+                               break;
+                       }
+                       pci_dev_put(tmp);
+               }
+       }
 
        /*
         * Devices on the root bus go through the iommu.  If that's not us,