diff mbox series

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

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

Commit Message

Joe Hattori Dec. 19, 2024, 3:28 a.m. UTC
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(-)

Comments

Frank Li Dec. 19, 2024, 5:59 p.m. UTC | #1
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 mbox series

Patch

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)