From eb7bb1226526640cfaef6f44ad18c704212d90ef Mon Sep 17 00:00:00 2001 From: Soohyun Kim Date: Fri, 4 Jul 2014 16:18:24 +0900 Subject: [PATCH] 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 --- drivers/dma/pl330.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) 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; } -- 2.20.1