Message ID | 20241219032845.2067851-1-joe@pf.is.s.u-tokyo.ac.jp (mailing list archive) |
---|---|
State | Superseded |
Headers | show |
Series | [v2] dmaengine: fsl-edma: implement the cleanup path of fsl_edma3_attach_pd() | expand |
On Thu, Dec 19, 2024 at 12:28:45PM +0900, Joe Hattori wrote: > 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(). > > This bug was found by an experimental verification tool that I am > developing. Please remove this sentense. Not reproducable by other people. Frank > > Fixes: 72f5801a4e2b ("dmaengine: fsl-edma: integrate v3 support") > Signed-off-by: Joe Hattori <joe@pf.is.s.u-tokyo.ac.jp> > --- > Changes in v2: > - Use devm_add_action_or_reset() to avoid goto in .probe(). > - Use fsl_edma3_detach_pd() in the error path of fsl_edma3_attach_pd() > - Update the commit message to elaborate the leak. > --- > drivers/dma/fsl-edma-common.h | 1 + > drivers/dma/fsl-edma-main.c | 41 ++++++++++++++++++++++++++++++----- > 2 files changed, 37 insertions(+), 5 deletions(-) > > diff --git a/drivers/dma/fsl-edma-common.h b/drivers/dma/fsl-edma-common.h > index ce37e1ee9c46..fe8f103d4a63 100644 > --- a/drivers/dma/fsl-edma-common.h > +++ b/drivers/dma/fsl-edma-common.h > @@ -166,6 +166,7 @@ struct fsl_edma_chan { > struct work_struct issue_worker; > struct platform_device *pdev; > struct device *pd_dev; > + struct device_link *pd_dev_link; > u32 srcid; > struct clk *clk; > int priority; > diff --git a/drivers/dma/fsl-edma-main.c b/drivers/dma/fsl-edma-main.c > index 60de1003193a..1a613236b3e4 100644 > --- a/drivers/dma/fsl-edma-main.c > +++ b/drivers/dma/fsl-edma-main.c > @@ -417,10 +417,33 @@ static const struct of_device_id 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) > { > struct fsl_edma_chan *fsl_chan; > - struct device_link *link; > struct device *pd_chan; > struct device *dev; > 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); > if (IS_ERR_OR_NULL(pd_chan)) { > 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_RPM_ACTIVE); > - if (!link) { > + if (!fsl_chan->pd_dev_link) { > 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; > @@ -455,6 +479,10 @@ static int fsl_edma3_attach_pd(struct platform_device *pdev, struct fsl_edma_eng > } > > return 0; > + > +detach: > + fsl_edma3_detach_pd(fsl_edma); > + return -EINVAL; > } > > 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); > if (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) > -- > 2.34.1 >
diff --git a/drivers/dma/fsl-edma-common.h b/drivers/dma/fsl-edma-common.h index ce37e1ee9c46..fe8f103d4a63 100644 --- a/drivers/dma/fsl-edma-common.h +++ b/drivers/dma/fsl-edma-common.h @@ -166,6 +166,7 @@ struct fsl_edma_chan { struct work_struct issue_worker; struct platform_device *pdev; struct device *pd_dev; + struct device_link *pd_dev_link; u32 srcid; struct clk *clk; int priority; diff --git a/drivers/dma/fsl-edma-main.c b/drivers/dma/fsl-edma-main.c index 60de1003193a..1a613236b3e4 100644 --- a/drivers/dma/fsl-edma-main.c +++ b/drivers/dma/fsl-edma-main.c @@ -417,10 +417,33 @@ static const struct of_device_id 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) { struct fsl_edma_chan *fsl_chan; - struct device_link *link; struct device *pd_chan; struct device *dev; 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); if (IS_ERR_OR_NULL(pd_chan)) { 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_RPM_ACTIVE); - if (!link) { + if (!fsl_chan->pd_dev_link) { 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; @@ -455,6 +479,10 @@ static int fsl_edma3_attach_pd(struct platform_device *pdev, struct fsl_edma_eng } return 0; + +detach: + fsl_edma3_detach_pd(fsl_edma); + return -EINVAL; } 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); if (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)
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(). This bug was found by an experimental verification tool that I am developing. Fixes: 72f5801a4e2b ("dmaengine: fsl-edma: integrate v3 support") Signed-off-by: Joe Hattori <joe@pf.is.s.u-tokyo.ac.jp> --- Changes in v2: - Use devm_add_action_or_reset() to avoid goto in .probe(). - Use fsl_edma3_detach_pd() in the error path of fsl_edma3_attach_pd() - Update the commit message to elaborate the leak. --- drivers/dma/fsl-edma-common.h | 1 + drivers/dma/fsl-edma-main.c | 41 ++++++++++++++++++++++++++++++----- 2 files changed, 37 insertions(+), 5 deletions(-)