diff options
author | Robin Gong <yibin.gong@nxp.com> | 2021-07-14 18:20:52 +0800 |
---|---|---|
committer | Shawn Guo <shawnguo@kernel.org> | 2021-07-23 11:20:06 +0800 |
commit | 4e2b10be1f4fe06c9deaaf2c03a05abcff191791 (patch) | |
tree | e6d8b67378953fcbb4726b9fb24eedba966c92a8 /drivers/dma | |
parent | b98ce2f4e32befa4999e180f48031d814f2a401c (diff) |
dmaengine: imx-sdma: add terminated list for freed descriptor in worker
Add terminated list for keeping descriptor so that it could be freed in
worker without any potential involving next descriptor raised up before
this descriptor freed, because vchan_get_all_descriptors get all
descriptors including the last terminated descriptor and the next
descriptor, hence, the next descriptor maybe freed unexpectly when it's
done in worker without this patch.
https://www.spinics.net/lists/dmaengine/msg23367.html
Signed-off-by: Robin Gong <yibin.gong@nxp.com>
Reported-by: Richard Leitner <richard.leitner@skidata.com>
Reviewed-by: Lucas Stach <l.stach@pengutronix.de>
Signed-off-by: Shawn Guo <shawnguo@kernel.org>
Diffstat (limited to 'drivers/dma')
-rw-r--r-- | drivers/dma/imx-sdma.c | 17 |
1 files changed, 10 insertions, 7 deletions
diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c index 9ec1edf73879..cacc725ca545 100644 --- a/drivers/dma/imx-sdma.c +++ b/drivers/dma/imx-sdma.c @@ -439,6 +439,7 @@ struct sdma_channel { enum dma_status status; struct imx_dma_data data; struct work_struct terminate_worker; + struct list_head terminated; bool is_ram_script; }; @@ -1107,9 +1108,6 @@ static void sdma_channel_terminate_work(struct work_struct *work) { struct sdma_channel *sdmac = container_of(work, struct sdma_channel, terminate_worker); - unsigned long flags; - LIST_HEAD(head); - /* * According to NXP R&D team a delay of one BD SDMA cost time * (maximum is 1ms) should be added after disable of the channel @@ -1118,10 +1116,7 @@ static void sdma_channel_terminate_work(struct work_struct *work) */ usleep_range(1000, 2000); - spin_lock_irqsave(&sdmac->vc.lock, flags); - vchan_get_all_descriptors(&sdmac->vc, &head); - spin_unlock_irqrestore(&sdmac->vc.lock, flags); - vchan_dma_desc_free_list(&sdmac->vc, &head); + vchan_dma_desc_free_list(&sdmac->vc, &sdmac->terminated); } static int sdma_terminate_all(struct dma_chan *chan) @@ -1135,6 +1130,13 @@ static int sdma_terminate_all(struct dma_chan *chan) if (sdmac->desc) { vchan_terminate_vdesc(&sdmac->desc->vd); + /* + * move out current descriptor into terminated list so that + * it could be free in sdma_channel_terminate_work alone + * later without potential involving next descriptor raised + * up before the last descriptor terminated. + */ + vchan_get_all_descriptors(&sdmac->vc, &sdmac->terminated); sdmac->desc = NULL; schedule_work(&sdmac->terminate_worker); } @@ -2130,6 +2132,7 @@ static int sdma_probe(struct platform_device *pdev) sdmac->channel = i; sdmac->vc.desc_free = sdma_desc_free; + INIT_LIST_HEAD(&sdmac->terminated); INIT_WORK(&sdmac->terminate_worker, sdma_channel_terminate_work); /* |