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

dmaengine: sprd: Fix the possible memory leak issue

If we terminate the channel to free all descriptors associated with this
channel, we will leak the memory of current descriptor if the current
descriptor is not completed, since it had been deteled from the desc_issued
list and have not been added into the desc_completed list.

Thus we should check if current descriptor is completed or not, when freeing
the descriptors associated with one channel, if not, we should free it to
avoid this issue.

Fixes: 9b3b8171f7f4 ("dmaengine: sprd: Add Spreadtrum DMA driver")
Reported-by: Zhenfang Wang <zhenfang.wang@unisoc.com>
Tested-by: Zhenfang Wang <zhenfang.wang@unisoc.com>
Signed-off-by: Baolin Wang <baolin.wang@linaro.org>
Link: https://lore.kernel.org/r/170dbbc6d5366b6fa974ce2d366652e23a334251.1570609788.git.baolin.wang@linaro.org
Signed-off-by: Vinod Koul <vkoul@kernel.org>

authored by

Baolin Wang and committed by
Vinod Koul
ec1ac309 6c6de1dd

+15
+15
drivers/dma/sprd-dma.c
··· 212 212 struct sprd_dma_chn channels[0]; 213 213 }; 214 214 215 + static void sprd_dma_free_desc(struct virt_dma_desc *vd); 215 216 static bool sprd_dma_filter_fn(struct dma_chan *chan, void *param); 216 217 static struct of_dma_filter_info sprd_dma_info = { 217 218 .filter_fn = sprd_dma_filter_fn, ··· 614 613 static void sprd_dma_free_chan_resources(struct dma_chan *chan) 615 614 { 616 615 struct sprd_dma_chn *schan = to_sprd_dma_chan(chan); 616 + struct virt_dma_desc *cur_vd = NULL; 617 617 unsigned long flags; 618 618 619 619 spin_lock_irqsave(&schan->vc.lock, flags); 620 + if (schan->cur_desc) 621 + cur_vd = &schan->cur_desc->vd; 622 + 620 623 sprd_dma_stop(schan); 621 624 spin_unlock_irqrestore(&schan->vc.lock, flags); 625 + 626 + if (cur_vd) 627 + sprd_dma_free_desc(cur_vd); 622 628 623 629 vchan_free_chan_resources(&schan->vc); 624 630 pm_runtime_put(chan->device->dev); ··· 1039 1031 static int sprd_dma_terminate_all(struct dma_chan *chan) 1040 1032 { 1041 1033 struct sprd_dma_chn *schan = to_sprd_dma_chan(chan); 1034 + struct virt_dma_desc *cur_vd = NULL; 1042 1035 unsigned long flags; 1043 1036 LIST_HEAD(head); 1044 1037 1045 1038 spin_lock_irqsave(&schan->vc.lock, flags); 1039 + if (schan->cur_desc) 1040 + cur_vd = &schan->cur_desc->vd; 1041 + 1046 1042 sprd_dma_stop(schan); 1047 1043 1048 1044 vchan_get_all_descriptors(&schan->vc, &head); 1049 1045 spin_unlock_irqrestore(&schan->vc.lock, flags); 1046 + 1047 + if (cur_vd) 1048 + sprd_dma_free_desc(cur_vd); 1050 1049 1051 1050 vchan_dma_desc_free_list(&schan->vc, &head); 1052 1051 return 0;