summaryrefslogtreecommitdiff
path: root/drivers/dma
diff options
context:
space:
mode:
authorSascha Hauer <s.hauer@pengutronix.de>2019-12-16 11:53:28 +0100
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2020-02-24 08:34:44 +0100
commitaf8eca600b408a0e59d2848dfcfad60413f626a9 (patch)
tree6d335acee7de971c1641eda12703ea880b780048 /drivers/dma
parent219b3d29532582df5b55aa3c3552820fdac1d4a3 (diff)
dmaengine: imx-sdma: Fix memory leak
[ Upstream commit 02939cd167095f16328a1bd5cab5a90b550606df ] The current descriptor is not on any list of the virtual DMA channel. Once sdma_terminate_all() is called when a descriptor is currently in flight then this one is forgotten to be freed. We have to call vchan_terminate_vdesc() on this descriptor to re-add it to the lists. Now that we also free the currently running descriptor we can (and actually have to) remove the current descriptor from its list also for the cyclic case. Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de> Reviewed-by: Robin Gong <yibin.gong@nxp.com> Tested-by: Robin Gong <yibin.gong@nxp.com> Link: https://lore.kernel.org/r/20191216105328.15198-10-s.hauer@pengutronix.de Signed-off-by: Vinod Koul <vkoul@kernel.org> Signed-off-by: Sasha Levin <sashal@kernel.org>
Diffstat (limited to 'drivers/dma')
-rw-r--r--drivers/dma/imx-sdma.c19
1 files changed, 11 insertions, 8 deletions
diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c
index ceb82e74f5b4..d66a7fdff898 100644
--- a/drivers/dma/imx-sdma.c
+++ b/drivers/dma/imx-sdma.c
@@ -738,12 +738,8 @@ static void sdma_start_desc(struct sdma_channel *sdmac)
return;
}
sdmac->desc = desc = to_sdma_desc(&vd->tx);
- /*
- * Do not delete the node in desc_issued list in cyclic mode, otherwise
- * the desc allocated will never be freed in vchan_dma_desc_free_list
- */
- if (!(sdmac->flags & IMX_DMA_SG_LOOP))
- list_del(&vd->node);
+
+ list_del(&vd->node);
sdma->channel_control[channel].base_bd_ptr = desc->bd_phys;
sdma->channel_control[channel].current_bd_ptr = desc->bd_phys;
@@ -1044,7 +1040,6 @@ static void sdma_channel_terminate_work(struct work_struct *work)
spin_lock_irqsave(&sdmac->vc.lock, flags);
vchan_get_all_descriptors(&sdmac->vc, &head);
- sdmac->desc = NULL;
spin_unlock_irqrestore(&sdmac->vc.lock, flags);
vchan_dma_desc_free_list(&sdmac->vc, &head);
}
@@ -1052,11 +1047,19 @@ static void sdma_channel_terminate_work(struct work_struct *work)
static int sdma_disable_channel_async(struct dma_chan *chan)
{
struct sdma_channel *sdmac = to_sdma_chan(chan);
+ unsigned long flags;
+
+ spin_lock_irqsave(&sdmac->vc.lock, flags);
sdma_disable_channel(chan);
- if (sdmac->desc)
+ if (sdmac->desc) {
+ vchan_terminate_vdesc(&sdmac->desc->vd);
+ sdmac->desc = NULL;
schedule_work(&sdmac->terminate_worker);
+ }
+
+ spin_unlock_irqrestore(&sdmac->vc.lock, flags);
return 0;
}