Add PM device links between Physical Function (PF) supplier and Virtual Function (VF) consumers via device_link_add() upon SR-IOV enablement. This ensures the PM core enforces the proper power management sequence: suspending VFs before the PF, and resuming the PF before VFs. If linking fails, roll back SR-IOV initialization.
Also add a comment clarifying that pci_disable_sriov() removes VF drivers before firmware VF contexts are destroyed. Signed-off-by: David Zhang <[email protected]> --- drivers/accel/amdxdna/aie4_sriov.c | 74 ++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/drivers/accel/amdxdna/aie4_sriov.c b/drivers/accel/amdxdna/aie4_sriov.c index 0eea28f62676..bfea6ff00ec0 100644 --- a/drivers/accel/amdxdna/aie4_sriov.c +++ b/drivers/accel/amdxdna/aie4_sriov.c @@ -56,9 +56,75 @@ int aie4_sriov_stop(struct amdxdna_dev_hdl *ndev) pci_disable_sriov(pdev); ndev->num_vfs = 0; + + /* + * pci_disable_sriov() removes VF drivers first; call destroy_vfs after + * so firmware VF contexts are not cleared before VF drivers finish cleanup. + */ return aie4_destroy_vfs(ndev); } +static int aie4_for_each_vfs(struct amdxdna_dev *xdna, + int (*cb)(struct amdxdna_dev *, struct pci_dev *)) +{ + struct pci_dev *pdev_pf = to_pci_dev(xdna->ddev.dev); + struct pci_dev *pdev_vf; + int pos, ret; + u16 vf_did; + + pos = pci_find_ext_capability(pdev_pf, PCI_EXT_CAP_ID_SRIOV); + if (!pos) + return 0; + ret = pci_read_config_word(pdev_pf, pos + PCI_SRIOV_VF_DID, &vf_did); + if (ret) { + XDNA_ERR(xdna, "read VF Device ID failed %d", ret); + return -ENODEV; + } + + for (pdev_vf = pci_get_device(pdev_pf->vendor, vf_did, NULL); + pdev_vf; + pdev_vf = pci_get_device(pdev_pf->vendor, vf_did, pdev_vf)) { + if (!pdev_vf->is_virtfn || pdev_vf->physfn != pdev_pf) + continue; + + ret = cb(xdna, pdev_vf); + if (ret) { + /* + * On early return the next iteration never runs, so + * release the current device's ref manually. + * On normal loop exit pci_get_device() returning NULL + * already releases the last device's ref internally. + */ + pci_dev_put(pdev_vf); + return ret; + } + } + + return 0; +} + +static int aie4_link_vf(struct amdxdna_dev *xdna, struct pci_dev *pdev_vf) +{ + struct pci_dev *pdev_pf = to_pci_dev(xdna->ddev.dev); + struct device_link *link; + + link = device_link_add(&pdev_vf->dev, /* consumer = VF */ + &pdev_pf->dev, /* supplier = PF */ + DL_FLAG_PM_RUNTIME | DL_FLAG_AUTOREMOVE_CONSUMER); + if (!link) { + XDNA_ERR(xdna, "Failed to link VF %s", pci_name(pdev_vf)); + return -EINVAL; + } + + XDNA_DBG(xdna, "Linked VF %s", pci_name(pdev_vf)); + return 0; +} + +static int aie4_link_vfs(struct amdxdna_dev *xdna) +{ + return aie4_for_each_vfs(xdna, aie4_link_vf); +} + static int aie4_sriov_start(struct amdxdna_dev_hdl *ndev, int num_vfs) { struct amdxdna_dev *xdna = ndev->aie.xdna; @@ -76,6 +142,14 @@ static int aie4_sriov_start(struct amdxdna_dev_hdl *ndev, int num_vfs) return ret; } + ret = aie4_link_vfs(xdna); + if (ret) { + XDNA_ERR(xdna, "link VFs failed, ret: %d", ret); + pci_disable_sriov(pdev); + aie4_destroy_vfs(ndev); + return ret; + } + ndev->num_vfs = num_vfs; return num_vfs; } -- 2.34.1
