Commit d2a321f3 authored by Alexandre Bounine's avatar Alexandre Bounine Committed by Linus Torvalds

rapidio/tsi721_dma: fix pending transaction queue handling

Fix pending DMA request queue handling to avoid broken ordering during
concurrent request submissions.
Signed-off-by: default avatarAlexandre 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: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 9673b883
...@@ -674,7 +674,7 @@ struct tsi721_bdma_chan { ...@@ -674,7 +674,7 @@ struct tsi721_bdma_chan {
struct dma_chan dchan; struct dma_chan dchan;
struct tsi721_tx_desc *tx_desc; struct tsi721_tx_desc *tx_desc;
spinlock_t lock; spinlock_t lock;
struct list_head active_list; struct tsi721_tx_desc *active_tx;
struct list_head queue; struct list_head queue;
struct list_head free_list; struct list_head free_list;
struct tasklet_struct tasklet; struct tasklet_struct tasklet;
......
...@@ -63,14 +63,6 @@ struct tsi721_tx_desc *to_tsi721_desc(struct dma_async_tx_descriptor *txd) ...@@ -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); 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) static int tsi721_bdma_ch_init(struct tsi721_bdma_chan *bdma_chan, int bd_num)
{ {
struct tsi721_dma_desc *bd_ptr; struct tsi721_dma_desc *bd_ptr;
...@@ -534,23 +526,30 @@ static int tsi721_submit_sg(struct tsi721_tx_desc *desc) ...@@ -534,23 +526,30 @@ static int tsi721_submit_sg(struct tsi721_tx_desc *desc)
return err; 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; int err;
dev_dbg(bdma_chan->dchan.device->dev, "%s: Enter\n", __func__); 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 * If there is no data transfer in progress, fetch new descriptor from
* into the processing list * the pending queue.
*/ */
if (!list_empty(&bdma_chan->queue))
list_splice_init(&bdma_chan->queue, &bdma_chan->active_list); 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 (desc) {
if (!list_empty(&bdma_chan->active_list)) {
desc = tsi721_dma_first_active(bdma_chan);
err = tsi721_submit_sg(desc); err = tsi721_submit_sg(desc);
if (!err) if (!err)
tsi721_start_dma(bdma_chan); tsi721_start_dma(bdma_chan);
...@@ -581,6 +580,10 @@ static void tsi721_dma_tasklet(unsigned long data) ...@@ -581,6 +580,10 @@ static void tsi721_dma_tasklet(unsigned long data)
dev_err(bdma_chan->dchan.device->dev, dev_err(bdma_chan->dchan.device->dev,
"%s: DMA ERROR - DMAC%d_STS = 0x%x\n", "%s: DMA ERROR - DMAC%d_STS = 0x%x\n",
__func__, bdma_chan->id, dmac_sts); __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) { if (dmac_int & TSI721_DMAC_INT_STFULL) {
...@@ -594,7 +597,7 @@ static void tsi721_dma_tasklet(unsigned long data) ...@@ -594,7 +597,7 @@ static void tsi721_dma_tasklet(unsigned long data)
tsi721_clr_stat(bdma_chan); tsi721_clr_stat(bdma_chan);
spin_lock(&bdma_chan->lock); spin_lock(&bdma_chan->lock);
desc = tsi721_dma_first_active(bdma_chan); desc = bdma_chan->active_tx;
if (desc->sg_len == 0) { if (desc->sg_len == 0) {
dma_async_tx_callback callback = NULL; dma_async_tx_callback callback = NULL;
...@@ -606,14 +609,15 @@ static void tsi721_dma_tasklet(unsigned long data) ...@@ -606,14 +609,15 @@ static void tsi721_dma_tasklet(unsigned long data)
callback = desc->txd.callback; callback = desc->txd.callback;
param = desc->txd.callback_param; 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); spin_unlock(&bdma_chan->lock);
if (callback) if (callback)
callback(param); callback(param);
spin_lock(&bdma_chan->lock); spin_lock(&bdma_chan->lock);
} }
tsi721_advance_work(bdma_chan); tsi721_advance_work(bdma_chan, bdma_chan->active_tx);
spin_unlock(&bdma_chan->lock); spin_unlock(&bdma_chan->lock);
} }
...@@ -720,9 +724,6 @@ static void tsi721_free_chan_resources(struct dma_chan *dchan) ...@@ -720,9 +724,6 @@ static void tsi721_free_chan_resources(struct dma_chan *dchan)
if (bdma_chan->bd_base == NULL) if (bdma_chan->bd_base == NULL)
return; return;
BUG_ON(!list_empty(&bdma_chan->active_list));
BUG_ON(!list_empty(&bdma_chan->queue));
tsi721_bdma_interrupt_enable(bdma_chan, 0); tsi721_bdma_interrupt_enable(bdma_chan, 0);
bdma_chan->active = false; bdma_chan->active = false;
tsi721_sync_dma_irq(bdma_chan); tsi721_sync_dma_irq(bdma_chan);
...@@ -745,11 +746,11 @@ static void tsi721_issue_pending(struct dma_chan *dchan) ...@@ -745,11 +746,11 @@ static void tsi721_issue_pending(struct dma_chan *dchan)
dev_dbg(dchan->device->dev, "%s: Enter\n", __func__); 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) { if (tsi721_dma_is_idle(bdma_chan) && bdma_chan->active) {
spin_lock_bh(&bdma_chan->lock); tsi721_advance_work(bdma_chan, NULL);
tsi721_advance_work(bdma_chan);
spin_unlock_bh(&bdma_chan->lock);
} }
spin_unlock_bh(&bdma_chan->lock);
} }
static static
...@@ -839,7 +840,8 @@ static int tsi721_terminate_all(struct dma_chan *dchan) ...@@ -839,7 +840,8 @@ static int tsi721_terminate_all(struct dma_chan *dchan)
} while ((dmac_int & TSI721_DMAC_INT_SUSP) == 0); } 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_splice_init(&bdma_chan->queue, &list);
list_for_each_entry_safe(desc, _d, &list, desc_node) list_for_each_entry_safe(desc, _d, &list, desc_node)
...@@ -875,7 +877,7 @@ int tsi721_register_dma(struct tsi721_device *priv) ...@@ -875,7 +877,7 @@ int tsi721_register_dma(struct tsi721_device *priv)
spin_lock_init(&bdma_chan->lock); 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->queue);
INIT_LIST_HEAD(&bdma_chan->free_list); INIT_LIST_HEAD(&bdma_chan->free_list);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment