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

Reply via email to