DMA: pl330: Fixed wrong operation in pl330 interrupt handler
authorSoohyun Kim <soohyuni.kim@samsung.com>
Fri, 4 Jul 2014 07:18:24 +0000 (16:18 +0900)
committerTaekki Kim <taekki.kim@samsung.com>
Mon, 14 May 2018 05:42:46 +0000 (14:42 +0900)
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 <soohyuni.kim@samsung.com>
Signed-off-by: Seokju Yoon <sukju.yoon@samsung.com>
drivers/dma/pl330.c

index b17b42c4a8cb74186aa37450dc13fec2ac595a25..5f58869727023c963db758817135e083b83ea181 100644 (file)
@@ -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;
 }