diff options
| author | Zhijian Han <hanzhijian1991@gmail.com> | 2026-09-04 09:23:07 +0800 |
|---|---|---|
| committer | Bjorn Helgaas <bhelgaas@google.com> | 2026-09-22 12:14:53 -0500 |
| commit | 1e184f38851ff6fac0b72bfb7f51db47ed6f257f (patch) | |
| tree | 6f6fff4c88e6bf22d52a0a285e517700dcfd6988 /drivers/pci/controller/dwc | |
| parent | 0031f76f73b86f711d0e4e95a967aea1821fb867 (diff) | |
| download | linux-next-1e184f38851ff6fac0b72bfb7f51db47ed6f257f.tar.gz linux-next-1e184f38851ff6fac0b72bfb7f51db47ed6f257f.zip | |
PCI: imx6: Fix resource leaks in probe error paths
imx_pcie_probe() leaks both pwrctrl devices and power domains on failure:
- imx_pcie_attach_pd() attaches the "pcie" and "pcie_phy" power domains
and adds device links to them, but nothing detaches the domains or
removes the links on probe failure or deferral, so they leak.
- A failure of devm_pm_runtime_set_active_enabled() returns directly
without destroying the pwrctrl devices.
- A partial failure inside imx_pcie_attach_pd() leaks the power domains
that were already attached.
Add imx_pcie_detach_pd_action() to remove the device links and detach the
power domains in reverse order of acquisition and register it with
devm_add_action_or_reset() so they are released automatically on probe
failure.
Fixes: 2c5768344f88 ("PCI: imx6: Move pci_pwrctrl_create_devices() to imx_pcie_probe()")
Reported-by: sashiko-bot@kernel.org
Link: https://lore.kernel.org/all/20260822013640.182C01F000E9@smtp.kernel.org/
Signed-off-by: Zhijian Han <hanzhijian1991@gmail.com>
[mani: removed the imx_pcie_detach_pd() helper]
Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com>
[bhelgaas: update commit log to match inlining of imx_pcie_detach_pd() into
imx_pcie_detach_pd_action()]
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Link: https://patch.msgid.link/20260904012307.3434449-1-hanzhijian1991@gmail.com
Diffstat (limited to 'drivers/pci/controller/dwc')
| -rw-r--r-- | drivers/pci/controller/dwc/pci-imx6.c | 54 |
1 files changed, 43 insertions, 11 deletions
diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c index 616c42932faa..6cf9106cce69 100644 --- a/drivers/pci/controller/dwc/pci-imx6.c +++ b/drivers/pci/controller/dwc/pci-imx6.c @@ -180,8 +180,12 @@ struct imx_pcie { struct imx_lut_data luts[IMX95_MAX_LUT]; /* power domain for pcie */ struct device *pd_pcie; + /* device link for pcie power domain */ + struct device_link *pd_link; /* power domain for pcie phy */ struct device *pd_pcie_phy; + /* device link for pcie phy power domain */ + struct device_link *pd_phy_link; struct phy *phy; const struct imx_pcie_drvdata *drvdata; @@ -639,10 +643,34 @@ static int imx6q_pcie_abort_handler(unsigned long addr, } #endif +static void imx_pcie_detach_pd_action(void *data) +{ + struct imx_pcie *imx_pcie = data; + + if (!IS_ERR_OR_NULL(imx_pcie->pd_phy_link)) { + device_link_del(imx_pcie->pd_phy_link); + imx_pcie->pd_phy_link = NULL; + } + + if (!IS_ERR_OR_NULL(imx_pcie->pd_link)) { + device_link_del(imx_pcie->pd_link); + imx_pcie->pd_link = NULL; + } + + if (!IS_ERR_OR_NULL(imx_pcie->pd_pcie_phy)) { + dev_pm_domain_detach(imx_pcie->pd_pcie_phy, true); + imx_pcie->pd_pcie_phy = NULL; + } + + if (!IS_ERR_OR_NULL(imx_pcie->pd_pcie)) { + dev_pm_domain_detach(imx_pcie->pd_pcie, true); + imx_pcie->pd_pcie = NULL; + } +} + static int imx_pcie_attach_pd(struct device *dev) { struct imx_pcie *imx_pcie = dev_get_drvdata(dev); - struct device_link *link; /* Do nothing when in a single power domain */ if (dev->pm_domain) @@ -654,11 +682,11 @@ static int imx_pcie_attach_pd(struct device *dev) /* Do nothing when power domain missing */ if (!imx_pcie->pd_pcie) return 0; - link = device_link_add(dev, imx_pcie->pd_pcie, - DL_FLAG_STATELESS | - DL_FLAG_PM_RUNTIME | - DL_FLAG_RPM_ACTIVE); - if (!link) { + imx_pcie->pd_link = device_link_add(dev, imx_pcie->pd_pcie, + DL_FLAG_STATELESS | + DL_FLAG_PM_RUNTIME | + DL_FLAG_RPM_ACTIVE); + if (!imx_pcie->pd_link) { dev_err(dev, "Failed to add device_link to pcie pd\n"); return -EINVAL; } @@ -667,11 +695,11 @@ static int imx_pcie_attach_pd(struct device *dev) if (IS_ERR(imx_pcie->pd_pcie_phy)) return PTR_ERR(imx_pcie->pd_pcie_phy); - link = device_link_add(dev, imx_pcie->pd_pcie_phy, - DL_FLAG_STATELESS | - DL_FLAG_PM_RUNTIME | - DL_FLAG_RPM_ACTIVE); - if (!link) { + imx_pcie->pd_phy_link = device_link_add(dev, imx_pcie->pd_pcie_phy, + DL_FLAG_STATELESS | + DL_FLAG_PM_RUNTIME | + DL_FLAG_RPM_ACTIVE); + if (!imx_pcie->pd_phy_link) { dev_err(dev, "Failed to add device_link to pcie_phy pd\n"); return -EINVAL; } @@ -1951,6 +1979,10 @@ static int imx_pcie_probe(struct platform_device *pdev) platform_set_drvdata(pdev, imx_pcie); + ret = devm_add_action_or_reset(dev, imx_pcie_detach_pd_action, imx_pcie); + if (ret) + return ret; + ret = imx_pcie_attach_pd(dev); if (ret) return ret; |
