[PATCH v3] PCI: qcom-ep: Fix runtime PM reference leak in probe error paths

From: Wentao Liang

Date: Wed Jun 24 2026 - 09:38:33 EST


The qcom_pcie_ep_probe() function obtains a runtime PM reference via
pm_runtime_get_noresume() at the beginning of the probe, but this
reference is only released on the successful completion path using
pm_runtime_put_sync().

However, the error paths fail to release this reference. The devres
cleanup registered by devm_pm_runtime_enable() only calls
pm_runtime_disable() and does not decrement the usage_count. As a
result, if any error occurs during probe (e.g., resource acquisition
failure or endpoint initialization failure), the reference is leaked,
permanently elevating the device's usage_count. This prevents proper
runtime suspend and clean device removal.

Fix this by properly distinguishing error paths that occur before and
after devm_pm_runtime_enable() succeeds:

- For errors before devm_pm_runtime_enable() succeeds: call
pm_runtime_put_noidle() and pm_runtime_disable() manually to clean
up the initial reference and disable runtime PM.

- For errors after devm_pm_runtime_enable() succeeds: only call
pm_runtime_put_sync() to drop the initial reference. The
pm_runtime_disable() is left to the devres cleanup handler,
avoiding a double-disable.

- On the successful probe path: call pm_runtime_put_sync() to drop
the initial reference. The return value of pm_runtime_put_sync()
is ignored because errors like -EAGAIN or -EBUSY only indicate
that the device cannot be suspended at this moment and do not
represent a fatal probe failure.

This ensures the runtime PM reference is correctly released on all
error paths without introducing double-disables or double-puts.

Cc: stable@xxxxxxxxxxxxxxx
Fixes: 5b026a9e714d ("PCI: qcom-ep: Add support for firmware-managed PCIe Endpoint")
Signed-off-by: Wentao Liang <vulab@xxxxxxxxxxx>
---
drivers/pci/controller/dwc/pcie-qcom-ep.c | 17 +++++++++++------
1 file changed, 11 insertions(+), 6 deletions(-)

diff --git a/drivers/pci/controller/dwc/pcie-qcom-ep.c b/drivers/pci/controller/dwc/pcie-qcom-ep.c
index 257c2bcb5f76..ff31f14b92a3 100644
--- a/drivers/pci/controller/dwc/pcie-qcom-ep.c
+++ b/drivers/pci/controller/dwc/pcie-qcom-ep.c
@@ -892,16 +892,16 @@ static int qcom_pcie_ep_probe(struct platform_device *pdev)
pm_runtime_set_active(dev);
ret = devm_pm_runtime_enable(dev);
if (ret)
- return ret;
+ goto err_manual_cleanup;

ret = qcom_pcie_ep_get_resources(pdev, pcie_ep);
if (ret)
- return ret;
+ goto err_put_ref;

ret = dw_pcie_ep_init(&pcie_ep->pci.ep);
if (ret) {
dev_err(dev, "Failed to initialize endpoint: %d\n", ret);
- return ret;
+ goto err_put_ref;
}

ret = qcom_pcie_ep_enable_irq_resources(pdev, pcie_ep);
@@ -915,10 +915,8 @@ static int qcom_pcie_ep_probe(struct platform_device *pdev)
}

ret = pm_runtime_put_sync(dev);
- if (ret < 0) {
+ if (ret < 0)
dev_err(dev, "Failed to suspend device: %d\n", ret);
- goto err_disable_irqs;
- }

pcie_ep->debugfs = debugfs_create_dir(name, NULL);
qcom_pcie_ep_init_debugfs(pcie_ep);
@@ -932,6 +930,13 @@ static int qcom_pcie_ep_probe(struct platform_device *pdev)
err_ep_deinit:
dw_pcie_ep_deinit(&pcie_ep->pci.ep);

+err_put_ref:
+ pm_runtime_put_sync(dev);
+ return ret;
+
+err_manual_cleanup:
+ pm_runtime_put_noidle(dev);
+ pm_runtime_disable(dev);
return ret;
}

--
2.39.5 (Apple Git-154)