]> www.infradead.org Git - users/dwmw2/linux.git/commitdiff
dmaengine: fsl-edma: implement the cleanup path of fsl_edma3_attach_pd()
authorJoe Hattori <joe@pf.is.s.u-tokyo.ac.jp>
Sat, 21 Dec 2024 07:57:12 +0000 (16:57 +0900)
committerVinod Koul <vkoul@kernel.org>
Tue, 24 Dec 2024 09:25:57 +0000 (14:55 +0530)
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: 72f5801a4e2b ("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>
drivers/dma/fsl-edma-common.h
drivers/dma/fsl-edma-main.c

index ce37e1ee9c462dc139b3b6e402c5170add73ced7..fe8f103d4a6378ee1fca710059dac18daf988ac3 100644 (file)
@@ -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;
index 60de1003193aa3420e7b46edc47c44c98d01ddf3..1a613236b3e4186a4ff71d9ff8ade7676a73146b 100644 (file)
@@ -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)