[PATCH V0 19/21] accel/amdxdna: Link SR-IOV VFs for power management sequencing
From: David Zhang
Date: Fri Sep 25 2026 - 21:36:38 EST
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 <yidong.zhang@xxxxxxx>
---
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