rapidio/tsi721_dma: fix pending transaction queue handling
authorAlexandre Bounine <alexandre.bounine@idt.com>
Tue, 22 Mar 2016 21:25:57 +0000 (14:25 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Tue, 22 Mar 2016 22:36:02 +0000 (15:36 -0700)
Fix pending DMA request queue handling to avoid broken ordering during
concurrent request submissions.

Signed-off-by: Alexandre Bounine <alexandre.bounine@idt.com>
Cc: Matt Porter <mporter@kernel.crashing.org>
Cc: Aurelien Jacquiot <a-jacquiot@ti.com>
Cc: Andre van Herk <andre.van.herk@prodrive-technologies.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
drivers/rapidio/devices/tsi721.h
drivers/rapidio/devices/tsi721_dma.c

index f81d011..d675a44 100644 (file)
@@ -674,7 +674,7 @@ struct tsi721_bdma_chan {
        struct dma_chan         dchan;
        struct tsi721_tx_desc   *tx_desc;
        spinlock_t              lock;
-       struct list_head        active_list;
+       struct tsi721_tx_desc   *active_tx;
        struct list_head        queue;
        struct list_head        free_list;
        struct tasklet_struct   tasklet;
index 4729594..500e1e0 100644 (file)
@@ -63,14 +63,6 @@ struct tsi721_tx_desc *to_tsi721_desc(struct dma_async_tx_descriptor *txd)
        return container_of(txd, struct tsi721_tx_desc, txd);
 }
 
-static inline
-struct tsi721_tx_desc *tsi721_dma_first_active(
-                               struct tsi721_bdma_chan *bdma_chan)
-{
-       return list_first_entry(&bdma_chan->active_list,
-                               struct tsi721_tx_desc, desc_node);
-}
-
 static int tsi721_bdma_ch_init(struct tsi721_bdma_chan *bdma_chan, int bd_num)
 {
        struct tsi721_dma_desc *bd_ptr;
@@ -534,23 +526,30 @@ entry_done:
        return err;
 }
 
-static void tsi721_advance_work(struct tsi721_bdma_chan *bdma_chan)
+static void tsi721_advance_work(struct tsi721_bdma_chan *bdma_chan,
+                               struct tsi721_tx_desc *desc)
 {
-       struct tsi721_tx_desc *desc;
        int err;
 
        dev_dbg(bdma_chan->dchan.device->dev, "%s: Enter\n", __func__);
 
+       if (!tsi721_dma_is_idle(bdma_chan))
+               return;
+
        /*
-        * If there are any new transactions in the queue add them
-        * into the processing list
-        */
-       if (!list_empty(&bdma_chan->queue))
-               list_splice_init(&bdma_chan->queue, &bdma_chan->active_list);
+        * If there is no data transfer in progress, fetch new descriptor from
+        * the pending queue.
+       */
+
+       if (desc == NULL && bdma_chan->active_tx == NULL &&
+                                       !list_empty(&bdma_chan->queue)) {
+               desc = list_first_entry(&bdma_chan->queue,
+                                       struct tsi721_tx_desc, desc_node);
+               list_del_init((&desc->desc_node));
+               bdma_chan->active_tx = desc;
+       }
 
-       /* Start new transaction (if available) */
-       if (!list_empty(&bdma_chan->active_list)) {
-               desc = tsi721_dma_first_active(bdma_chan);
+       if (desc) {
                err = tsi721_submit_sg(desc);
                if (!err)
                        tsi721_start_dma(bdma_chan);
@@ -581,6 +580,10 @@ static void tsi721_dma_tasklet(unsigned long data)
                dev_err(bdma_chan->dchan.device->dev,
                        "%s: DMA ERROR - DMAC%d_STS = 0x%x\n",
                        __func__, bdma_chan->id, dmac_sts);
+
+               spin_lock(&bdma_chan->lock);
+               bdma_chan->active_tx = NULL;
+               spin_unlock(&bdma_chan->lock);
        }
 
        if (dmac_int & TSI721_DMAC_INT_STFULL) {
@@ -594,7 +597,7 @@ static void tsi721_dma_tasklet(unsigned long data)
 
                tsi721_clr_stat(bdma_chan);
                spin_lock(&bdma_chan->lock);
-               desc = tsi721_dma_first_active(bdma_chan);
+               desc = bdma_chan->active_tx;
 
                if (desc->sg_len == 0) {
                        dma_async_tx_callback callback = NULL;
@@ -606,14 +609,15 @@ static void tsi721_dma_tasklet(unsigned long data)
                                callback = desc->txd.callback;
                                param = desc->txd.callback_param;
                        }
-                       list_move(&desc->desc_node, &bdma_chan->free_list);
+                       list_add(&desc->desc_node, &bdma_chan->free_list);
+                       bdma_chan->active_tx = NULL;
                        spin_unlock(&bdma_chan->lock);
                        if (callback)
                                callback(param);
                        spin_lock(&bdma_chan->lock);
                }
 
-               tsi721_advance_work(bdma_chan);
+               tsi721_advance_work(bdma_chan, bdma_chan->active_tx);
                spin_unlock(&bdma_chan->lock);
        }
 
@@ -720,9 +724,6 @@ static void tsi721_free_chan_resources(struct dma_chan *dchan)
        if (bdma_chan->bd_base == NULL)
                return;
 
-       BUG_ON(!list_empty(&bdma_chan->active_list));
-       BUG_ON(!list_empty(&bdma_chan->queue));
-
        tsi721_bdma_interrupt_enable(bdma_chan, 0);
        bdma_chan->active = false;
        tsi721_sync_dma_irq(bdma_chan);
@@ -745,11 +746,11 @@ static void tsi721_issue_pending(struct dma_chan *dchan)
 
        dev_dbg(dchan->device->dev, "%s: Enter\n", __func__);
 
+       spin_lock_bh(&bdma_chan->lock);
        if (tsi721_dma_is_idle(bdma_chan) && bdma_chan->active) {
-               spin_lock_bh(&bdma_chan->lock);
-               tsi721_advance_work(bdma_chan);
-               spin_unlock_bh(&bdma_chan->lock);
+               tsi721_advance_work(bdma_chan, NULL);
        }
+       spin_unlock_bh(&bdma_chan->lock);
 }
 
 static
@@ -839,7 +840,8 @@ static int tsi721_terminate_all(struct dma_chan *dchan)
                } while ((dmac_int & TSI721_DMAC_INT_SUSP) == 0);
        }
 
-       list_splice_init(&bdma_chan->active_list, &list);
+       if (bdma_chan->active_tx)
+               list_add(&bdma_chan->active_tx->desc_node, &list);
        list_splice_init(&bdma_chan->queue, &list);
 
        list_for_each_entry_safe(desc, _d, &list, desc_node)
@@ -875,7 +877,7 @@ int tsi721_register_dma(struct tsi721_device *priv)
 
                spin_lock_init(&bdma_chan->lock);
 
-               INIT_LIST_HEAD(&bdma_chan->active_list);
+               bdma_chan->active_tx = NULL;
                INIT_LIST_HEAD(&bdma_chan->queue);
                INIT_LIST_HEAD(&bdma_chan->free_list);