Message ID | 20241214040113.626275-1-joe@pf.is.s.u-tokyo.ac.jp (mailing list archive) |
---|---|
State | New |
Headers | show |
Series | dmaengine: fsl-edma: implement the cleanup path of fsl_edma3_attach_pd() | expand |
On Sat, Dec 14, 2024 at 01:01:13PM +0900, Joe Hattori wrote: > Current implementation of fsl_edma3_attach_pd() does not provide a > cleanup path, resulting in memory leak. Thus, implement an error path of > fsl_edma3_attach_pd(), which detaches PM domains, removes device links, > and unwind the runtime PM settings. Also add fsl_edma3_detach_pd() and > call it on the error path of the .probe(). > > This bug was found by an experimental static analysis tool that I am > developing. static analysis often false alarm. Descript problem itself. for example how memory leak happen when call func1(), .. func2()... > > Fixes: 72f5801a4e2b ("dmaengine: fsl-edma: integrate v3 support") > Signed-off-by: Joe Hattori <joe@pf.is.s.u-tokyo.ac.jp> > --- > drivers/dma/fsl-edma-common.h | 1 + > drivers/dma/fsl-edma-main.c | 54 ++++++++++++++++++++++++++++------- > 2 files changed, 45 insertions(+), 10 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..5204c06589ea 100644 > --- a/drivers/dma/fsl-edma-main.c > +++ b/drivers/dma/fsl-edma-main.c > @@ -420,7 +420,6 @@ MODULE_DEVICE_TABLE(of, fsl_edma_dt_ids); > 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 +435,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 err; > } > > - 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 err; > } > > fsl_chan->pd_dev = pd_chan; > @@ -455,6 +455,33 @@ static int fsl_edma3_attach_pd(struct platform_device *pdev, struct fsl_edma_eng > } > > return 0; > +err: > + while (--i >= 0) { > + if (fsl_edma->chan_masked & BIT(i)) > + continue; > + fsl_chan = &fsl_edma->chans[i]; > + dev_pm_domain_detach(fsl_chan->pd_dev, false); > + device_link_del(fsl_chan->pd_dev_link); > + pm_runtime_dont_use_autosuspend(fsl_chan->pd_dev); > + pm_runtime_set_suspended(fsl_chan->pd_dev); > + } can you call helper funciton fsl_edma3_detach_pd()? > + return -EINVAL; > +} > + > +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]; > + dev_pm_domain_detach(fsl_chan->pd_dev, false); > + device_link_del(fsl_chan->pd_dev_link); > + pm_runtime_dont_use_autosuspend(fsl_chan->pd_dev); > + pm_runtime_set_suspended(fsl_chan->pd_dev); > + } > } > > static int fsl_edma_probe(struct platform_device *pdev) > @@ -577,8 +604,10 @@ static int fsl_edma_probe(struct platform_device *pdev) > fsl_chan->clk = devm_clk_get_enabled(&pdev->dev, > (const char *)clk_name); > > - if (IS_ERR(fsl_chan->clk)) > - return PTR_ERR(fsl_chan->clk); > + if (IS_ERR(fsl_chan->clk)) { > + ret = PTR_ERR(fsl_chan->clk); > + goto err; > + } > } > fsl_chan->pdev = pdev; > vchan_init(&fsl_chan->vchan, &fsl_edma->dma_dev); > @@ -591,7 +620,7 @@ static int fsl_edma_probe(struct platform_device *pdev) > > ret = fsl_edma->drvdata->setup_irq(pdev, fsl_edma); > if (ret) > - return ret; > + goto err; > > dma_cap_set(DMA_PRIVATE, fsl_edma->dma_dev.cap_mask); > dma_cap_set(DMA_SLAVE, fsl_edma->dma_dev.cap_mask); > @@ -642,7 +671,7 @@ static int fsl_edma_probe(struct platform_device *pdev) > if (ret) { > dev_err(&pdev->dev, > "Can't register Freescale eDMA engine. (%d)\n", ret); > - return ret; > + goto err; > } > > ret = of_dma_controller_register(np, > @@ -652,7 +681,7 @@ static int fsl_edma_probe(struct platform_device *pdev) > dev_err(&pdev->dev, > "Can't register Freescale eDMA of_dma. (%d)\n", ret); > dma_async_device_unregister(&fsl_edma->dma_dev); > - return ret; > + goto err; > } > > /* enable round robin arbitration */ > @@ -660,6 +689,11 @@ static int fsl_edma_probe(struct platform_device *pdev) > edma_writel(fsl_edma, EDMA_CR_ERGA | EDMA_CR_ERCA, regs->cr); > > return 0; > + > +err: > + if (drvdata->flags & FSL_EDMA_DRV_HAS_PD) > + fsl_edma3_detach_pd(fsl_edma); > + return ret; Use devm_add_action() to avoid goto. Frank > } > > static void fsl_edma_remove(struct platform_device *pdev) > -- > 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..5204c06589ea 100644 --- a/drivers/dma/fsl-edma-main.c +++ b/drivers/dma/fsl-edma-main.c @@ -420,7 +420,6 @@ MODULE_DEVICE_TABLE(of, fsl_edma_dt_ids); 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 +435,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 err; } - 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 err; } fsl_chan->pd_dev = pd_chan; @@ -455,6 +455,33 @@ static int fsl_edma3_attach_pd(struct platform_device *pdev, struct fsl_edma_eng } return 0; +err: + while (--i >= 0) { + if (fsl_edma->chan_masked & BIT(i)) + continue; + fsl_chan = &fsl_edma->chans[i]; + dev_pm_domain_detach(fsl_chan->pd_dev, false); + device_link_del(fsl_chan->pd_dev_link); + pm_runtime_dont_use_autosuspend(fsl_chan->pd_dev); + pm_runtime_set_suspended(fsl_chan->pd_dev); + } + return -EINVAL; +} + +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]; + dev_pm_domain_detach(fsl_chan->pd_dev, false); + device_link_del(fsl_chan->pd_dev_link); + pm_runtime_dont_use_autosuspend(fsl_chan->pd_dev); + pm_runtime_set_suspended(fsl_chan->pd_dev); + } } static int fsl_edma_probe(struct platform_device *pdev) @@ -577,8 +604,10 @@ static int fsl_edma_probe(struct platform_device *pdev) fsl_chan->clk = devm_clk_get_enabled(&pdev->dev, (const char *)clk_name); - if (IS_ERR(fsl_chan->clk)) - return PTR_ERR(fsl_chan->clk); + if (IS_ERR(fsl_chan->clk)) { + ret = PTR_ERR(fsl_chan->clk); + goto err; + } } fsl_chan->pdev = pdev; vchan_init(&fsl_chan->vchan, &fsl_edma->dma_dev); @@ -591,7 +620,7 @@ static int fsl_edma_probe(struct platform_device *pdev) ret = fsl_edma->drvdata->setup_irq(pdev, fsl_edma); if (ret) - return ret; + goto err; dma_cap_set(DMA_PRIVATE, fsl_edma->dma_dev.cap_mask); dma_cap_set(DMA_SLAVE, fsl_edma->dma_dev.cap_mask); @@ -642,7 +671,7 @@ static int fsl_edma_probe(struct platform_device *pdev) if (ret) { dev_err(&pdev->dev, "Can't register Freescale eDMA engine. (%d)\n", ret); - return ret; + goto err; } ret = of_dma_controller_register(np, @@ -652,7 +681,7 @@ static int fsl_edma_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Can't register Freescale eDMA of_dma. (%d)\n", ret); dma_async_device_unregister(&fsl_edma->dma_dev); - return ret; + goto err; } /* enable round robin arbitration */ @@ -660,6 +689,11 @@ static int fsl_edma_probe(struct platform_device *pdev) edma_writel(fsl_edma, EDMA_CR_ERGA | EDMA_CR_ERCA, regs->cr); return 0; + +err: + if (drvdata->flags & FSL_EDMA_DRV_HAS_PD) + fsl_edma3_detach_pd(fsl_edma); + return ret; } static void fsl_edma_remove(struct platform_device *pdev)
Current implementation of fsl_edma3_attach_pd() does not provide a cleanup path, resulting in memory leak. Thus, implement an error path of fsl_edma3_attach_pd(), which detaches PM domains, removes device links, and unwind the runtime PM settings. Also add fsl_edma3_detach_pd() and call it on the error path of the .probe(). This bug was found by an experimental static analysis 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> --- drivers/dma/fsl-edma-common.h | 1 + drivers/dma/fsl-edma-main.c | 54 ++++++++++++++++++++++++++++------- 2 files changed, 45 insertions(+), 10 deletions(-)