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 <weiyang@xxxxxxxxxxxxxxxxxx> --- 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 -- To unsubscribe from this list: send the line "unsubscribe linux-pci" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html