Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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: 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>

authored by

Joe Hattori and committed by
Vinod Koul
ccfa3131 dcbef079

+37 -5
+1
drivers/dma/fsl-edma-common.h
··· 166 166 struct work_struct issue_worker; 167 167 struct platform_device *pdev; 168 168 struct device *pd_dev; 169 + struct device_link *pd_dev_link; 169 170 u32 srcid; 170 171 struct clk *clk; 171 172 int priority;
+36 -5
drivers/dma/fsl-edma-main.c
··· 417 417 }; 418 418 MODULE_DEVICE_TABLE(of, fsl_edma_dt_ids); 419 419 420 + static void fsl_edma3_detach_pd(struct fsl_edma_engine *fsl_edma) 421 + { 422 + struct fsl_edma_chan *fsl_chan; 423 + int i; 424 + 425 + for (i = 0; i < fsl_edma->n_chans; i++) { 426 + if (fsl_edma->chan_masked & BIT(i)) 427 + continue; 428 + fsl_chan = &fsl_edma->chans[i]; 429 + if (fsl_chan->pd_dev_link) 430 + device_link_del(fsl_chan->pd_dev_link); 431 + if (fsl_chan->pd_dev) { 432 + dev_pm_domain_detach(fsl_chan->pd_dev, false); 433 + pm_runtime_dont_use_autosuspend(fsl_chan->pd_dev); 434 + pm_runtime_set_suspended(fsl_chan->pd_dev); 435 + } 436 + } 437 + } 438 + 439 + static void devm_fsl_edma3_detach_pd(void *data) 440 + { 441 + fsl_edma3_detach_pd(data); 442 + } 443 + 420 444 static int fsl_edma3_attach_pd(struct platform_device *pdev, struct fsl_edma_engine *fsl_edma) 421 445 { 422 446 struct fsl_edma_chan *fsl_chan; 423 - struct device_link *link; 424 447 struct device *pd_chan; 425 448 struct device *dev; 426 449 int i; ··· 459 436 pd_chan = dev_pm_domain_attach_by_id(dev, i); 460 437 if (IS_ERR_OR_NULL(pd_chan)) { 461 438 dev_err(dev, "Failed attach pd %d\n", i); 462 - return -EINVAL; 439 + goto detach; 463 440 } 464 441 465 - link = device_link_add(dev, pd_chan, DL_FLAG_STATELESS | 442 + fsl_chan->pd_dev_link = device_link_add(dev, pd_chan, DL_FLAG_STATELESS | 466 443 DL_FLAG_PM_RUNTIME | 467 444 DL_FLAG_RPM_ACTIVE); 468 - if (!link) { 445 + if (!fsl_chan->pd_dev_link) { 469 446 dev_err(dev, "Failed to add device_link to %d\n", i); 470 - return -EINVAL; 447 + dev_pm_domain_detach(pd_chan, false); 448 + goto detach; 471 449 } 472 450 473 451 fsl_chan->pd_dev = pd_chan; ··· 479 455 } 480 456 481 457 return 0; 458 + 459 + detach: 460 + fsl_edma3_detach_pd(fsl_edma); 461 + return -EINVAL; 482 462 } 483 463 484 464 static int fsl_edma_probe(struct platform_device *pdev) ··· 570 542 571 543 if (drvdata->flags & FSL_EDMA_DRV_HAS_PD) { 572 544 ret = fsl_edma3_attach_pd(pdev, fsl_edma); 545 + if (ret) 546 + return ret; 547 + ret = devm_add_action_or_reset(&pdev->dev, devm_fsl_edma3_detach_pd, fsl_edma); 573 548 if (ret) 574 549 return ret; 575 550 }