Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -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;
+31 −29
Original line number Diff line number Diff line
@@ -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 @@ static int tsi721_submit_sg(struct tsi721_tx_desc *desc)
	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 there is no data transfer in progress, fetch new descriptor from
	 * the pending queue.
	*/
	if (!list_empty(&bdma_chan->queue))
		list_splice_init(&bdma_chan->queue, &bdma_chan->active_list);

	/* Start new transaction (if available) */
	if (!list_empty(&bdma_chan->active_list)) {
		desc = tsi721_dma_first_active(bdma_chan);
	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;
	}

	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__);

	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);
	if (tsi721_dma_is_idle(bdma_chan) && bdma_chan->active) {
		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);