The actual IOV BAR range is determined by the start address and the actual
size for vf_num VFs BAR. After shifting the IOV BAR, there would be a
chance the actual end address exceed the limit and overlap with other
devices.

This patch adds a check to make sure after shifting, the range will not
overlap with other devices.

Signed-off-by: Wei Yang <weiy...@linux.vnet.ibm.com>
---
 arch/powerpc/platforms/powernv/pci-ioda.c |   53 ++++++++++++++++++++++++++---
 1 file changed, 48 insertions(+), 5 deletions(-)

diff --git a/arch/powerpc/platforms/powernv/pci-ioda.c 
b/arch/powerpc/platforms/powernv/pci-ioda.c
index 8456ae8..1a1e74b 100644
--- a/arch/powerpc/platforms/powernv/pci-ioda.c
+++ b/arch/powerpc/platforms/powernv/pci-ioda.c
@@ -854,16 +854,18 @@ static unsigned int pnv_ioda_dma_weight(struct pci_dev 
*dev)
 }
 
 #ifdef CONFIG_PCI_IOV
-static void pnv_pci_vf_resource_shift(struct pci_dev *dev, int offset)
+static int pnv_pci_vf_resource_shift(struct pci_dev *dev, int offset)
 {
        struct pci_dn *pdn = pci_get_pdn(dev);
        int i;
        struct resource *res;
        resource_size_t size;
+       u16 vf_num;
 
        if (!dev->is_physfn)
-               return;
+               return -EINVAL;
 
+       vf_num = pdn->vf_pes;
        for (i = PCI_IOV_RESOURCES; i <= PCI_IOV_RESOURCE_END; i++) {
                res = &dev->resource[i];
                if (!res->flags || !res->parent)
@@ -875,11 +877,49 @@ static void pnv_pci_vf_resource_shift(struct pci_dev 
*dev, int offset)
                dev_info(&dev->dev, " Shifting VF BAR %pR to\n", res);
                size = pci_iov_resource_size(dev, i);
                res->start += size*offset;
-
                dev_info(&dev->dev, "                 %pR\n", res);
+
+               /*
+                * The actual IOV BAR range is determined by the start address
+                * and the actual size for vf_num VFs BAR. The check here is
+                * to make sure after shifting, the range will not overlap
+                * with other device.
+                */
+               if ((res->start + (size * vf_num)) > res->end) {
+                       dev_err(&dev->dev, "VF BAR%d: %pR will conflict with"
+                                       " other device after shift\n");
+                       goto failed;
+               }
+       }
+
+       for (i = PCI_IOV_RESOURCES; i <= PCI_IOV_RESOURCE_END; i++) {
+               res = &dev->resource[i];
+               if (!res->flags || !res->parent)
+                       continue;
+
+               if (!pnv_pci_is_mem_pref_64(res->flags))
+                       continue;
+
                pci_update_resource(dev, i);
        }
        pdn->max_vfs -= offset;
+       return 0;
+
+failed:
+       for (; i >= PCI_IOV_RESOURCES; i--) {
+               res = &dev->resource[i];
+               if (!res->flags || !res->parent)
+                       continue;
+
+               if (!pnv_pci_is_mem_pref_64(res->flags))
+                       continue;
+
+               dev_info(&dev->dev, " Shifting VF BAR %pR to\n", res);
+               size = pci_iov_resource_size(dev, i);
+               res->start += size*(-offset);
+               dev_info(&dev->dev, "                 %pR\n", res);
+       }
+       return -EBUSY;
 }
 #endif /* CONFIG_PCI_IOV */
 
@@ -1480,8 +1520,11 @@ int pnv_pci_sriov_enable(struct pci_dev *pdev, u16 
vf_num)
                }
 
                /* Do some magic shift */
-               if (pdn->m64_per_iov == 1)
-                       pnv_pci_vf_resource_shift(pdev, pdn->offset);
+               if (pdn->m64_per_iov == 1) {
+                       ret = pnv_pci_vf_resource_shift(pdev, pdn->offset);
+                       if (ret)
+                               goto m64_failed;
+               }
        }
 
        /* Setup VF PEs */
-- 
1.7.9.5

_______________________________________________
Linuxppc-dev mailing list
Linuxppc-dev@lists.ozlabs.org
https://lists.ozlabs.org/listinfo/linuxppc-dev

Reply via email to