From: Soohyun Kim Date: Fri, 4 Jul 2014 07:18:24 +0000 (+0900) Subject: DMA: pl330: Fixed wrong operation in pl330 interrupt handler X-Git-Url: https://git.stricted.de/?a=commitdiff_plain;h=eb7bb1226526640cfaef6f44ad18c704212d90ef;p=GitHub%2FLineageOS%2Fandroid_kernel_motorola_exynos9610.git DMA: pl330: Fixed wrong operation in pl330 interrupt handler Channel release operation can occur while running interrupt handler. It can causes the various side effect. This patch is adding check usage count to avoid above problem. Change-Id: I45a68b49837bd5abdb9a7404f2441e94393f2afe Signed-off-by: Soohyun Kim Signed-off-by: Seokju Yoon --- diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index b17b42c4a8cb..5f5886972702 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -487,6 +487,7 @@ struct pl330_dmac { /* Used the DMA wrapper */ bool wrapper; void __iomem *inst_wrapper; + int usage_count; /* Populated by the PL330 core driver during pl330_add */ struct pl330_config pcfg; @@ -1582,6 +1583,12 @@ static void pl330_dotask(unsigned long data) spin_lock_irqsave(&pl330->lock, flags); + if (!pl330->usage_count) { + pr_info("[%s] Channel is already free!\n",__func__); + spin_unlock_irqrestore(&pl330->lock, flags); + return; + } + /* The DMAC itself gone nuts */ if (pl330->dmac_tbd.reset_dmac) { pl330->state = DYING; @@ -1645,6 +1652,12 @@ static int pl330_update(struct pl330_dmac *pl330) spin_lock_irqsave(&pl330->lock, flags); + if (!pl330->usage_count) { + dev_err(pl330->ddma.dev, "%s:%d event is not exist!\n", __func__, __LINE__); + spin_unlock_irqrestore(&pl330->lock, flags); + return 0; + } + val = readl(regs + FSM) & 0x1; if (val) pl330->dmac_tbd.reset_mngr = true; @@ -1783,6 +1796,7 @@ static struct pl330_thread *pl330_request_channel(struct pl330_dmac *pl330) thrd->req[0].desc = NULL; thrd->req[1].desc = NULL; thrd->req_running = -1; + pl330->usage_count++; break; } } @@ -1799,8 +1813,10 @@ static inline void _free_event(struct pl330_thread *thrd, int ev) /* If the event is valid and was held by the thread */ if (ev >= 0 && ev < pl330->pcfg.num_events - && pl330->events[ev] == thrd->id) + && pl330->events[ev] == thrd->id) { pl330->events[ev] = -1; + pl330->usage_count--; + } } static void pl330_release_channel(struct pl330_thread *thrd) @@ -2013,6 +2029,7 @@ static int pl330_add(struct pl330_dmac *pl330) tasklet_init(&pl330->tasks, pl330_dotask, (unsigned long) pl330); pl330->state = INIT; + pl330->usage_count = 0; return 0; }