dmaengine: fsl-edma: implement the cleanup path of fsl_edma3_attach_pd()

Current implementation of fsl_edma3_attach_pd() does not provide a
cleanup path, resulting in a memory leak. For example,
dev_pm_domain_detach() is not called after dev_pm_domain_attach_by_id(),
and the device link created with the DL_FLAG_STATELESS is not released
explicitly.

Therefore, provide a cleanup function fsl_edma3_detach_pd() and call it
upon failure. Also add a devm_add_action_or_reset() call with this
function after a successful fsl_edma3_attach_pd().

Fixes: 72f5801a4e ("dmaengine: fsl-edma: integrate v3 support")
Signed-off-by: Joe Hattori <joe@pf.is.s.u-tokyo.ac.jp>
Link: https://lore.kernel.org/r/20241221075712.3297200-1-joe@pf.is.s.u-tokyo.ac.jp
Signed-off-by: Vinod Koul <vkoul@kernel.org>
This commit is contained in:
Joe Hattori 2024-12-21 16:57:12 +09:00 committed by Vinod Koul
parent dcbef0798e
commit ccfa3131d4
2 changed files with 37 additions and 5 deletions

View File

@ -166,6 +166,7 @@ struct fsl_edma_chan {
struct work_struct issue_worker; struct work_struct issue_worker;
struct platform_device *pdev; struct platform_device *pdev;
struct device *pd_dev; struct device *pd_dev;
struct device_link *pd_dev_link;
u32 srcid; u32 srcid;
struct clk *clk; struct clk *clk;
int priority; int priority;

View File

@ -417,10 +417,33 @@ static const struct of_device_id fsl_edma_dt_ids[] = {
}; };
MODULE_DEVICE_TABLE(of, fsl_edma_dt_ids); MODULE_DEVICE_TABLE(of, fsl_edma_dt_ids);
static void fsl_edma3_detach_pd(struct fsl_edma_engine *fsl_edma)
{
struct fsl_edma_chan *fsl_chan;
int i;
for (i = 0; i < fsl_edma->n_chans; i++) {
if (fsl_edma->chan_masked & BIT(i))
continue;
fsl_chan = &fsl_edma->chans[i];
if (fsl_chan->pd_dev_link)
device_link_del(fsl_chan->pd_dev_link);
if (fsl_chan->pd_dev) {
dev_pm_domain_detach(fsl_chan->pd_dev, false);
pm_runtime_dont_use_autosuspend(fsl_chan->pd_dev);
pm_runtime_set_suspended(fsl_chan->pd_dev);
}
}
}
static void devm_fsl_edma3_detach_pd(void *data)
{
fsl_edma3_detach_pd(data);
}
static int fsl_edma3_attach_pd(struct platform_device *pdev, struct fsl_edma_engine *fsl_edma) static int fsl_edma3_attach_pd(struct platform_device *pdev, struct fsl_edma_engine *fsl_edma)
{ {
struct fsl_edma_chan *fsl_chan; struct fsl_edma_chan *fsl_chan;
struct device_link *link;
struct device *pd_chan; struct device *pd_chan;
struct device *dev; struct device *dev;
int i; int i;
@ -436,15 +459,16 @@ static int fsl_edma3_attach_pd(struct platform_device *pdev, struct fsl_edma_eng
pd_chan = dev_pm_domain_attach_by_id(dev, i); pd_chan = dev_pm_domain_attach_by_id(dev, i);
if (IS_ERR_OR_NULL(pd_chan)) { if (IS_ERR_OR_NULL(pd_chan)) {
dev_err(dev, "Failed attach pd %d\n", i); dev_err(dev, "Failed attach pd %d\n", i);
return -EINVAL; goto detach;
} }
link = device_link_add(dev, pd_chan, DL_FLAG_STATELESS | fsl_chan->pd_dev_link = device_link_add(dev, pd_chan, DL_FLAG_STATELESS |
DL_FLAG_PM_RUNTIME | DL_FLAG_PM_RUNTIME |
DL_FLAG_RPM_ACTIVE); DL_FLAG_RPM_ACTIVE);
if (!link) { if (!fsl_chan->pd_dev_link) {
dev_err(dev, "Failed to add device_link to %d\n", i); dev_err(dev, "Failed to add device_link to %d\n", i);
return -EINVAL; dev_pm_domain_detach(pd_chan, false);
goto detach;
} }
fsl_chan->pd_dev = pd_chan; fsl_chan->pd_dev = pd_chan;
@ -455,6 +479,10 @@ static int fsl_edma3_attach_pd(struct platform_device *pdev, struct fsl_edma_eng
} }
return 0; return 0;
detach:
fsl_edma3_detach_pd(fsl_edma);
return -EINVAL;
} }
static int fsl_edma_probe(struct platform_device *pdev) static int fsl_edma_probe(struct platform_device *pdev)
@ -544,6 +572,9 @@ static int fsl_edma_probe(struct platform_device *pdev)
ret = fsl_edma3_attach_pd(pdev, fsl_edma); ret = fsl_edma3_attach_pd(pdev, fsl_edma);
if (ret) if (ret)
return ret; return ret;
ret = devm_add_action_or_reset(&pdev->dev, devm_fsl_edma3_detach_pd, fsl_edma);
if (ret)
return ret;
} }
if (drvdata->flags & FSL_EDMA_DRV_TCD64) if (drvdata->flags & FSL_EDMA_DRV_TCD64)