Subject: [PATCH v3] ARM: edma: fix residue race for cyclic

From: [email-masked] (Peter Ujfalusi)
Date: Wed, 27 Jan 2016 16:14:34 +0200

One nitpick

On 01/21/2016 06:22 PM, John Ogness wrote:
> When retrieving the residue value for cyclic transfers, the
> SRC/DST fields of the active PaRAM are read. However, the AM335x
> Technical Reference Manual states:
> 
>   11.3.3.6 Parameter Set Updates
> 
>   After the TR is read from the PaRAM (and is in the process
>   of being submitted to the EDMA3TC), the following fields are
>   updated as needed: ... SRC DST
> 
> This means SRC/DST is incremented even though the DMA transfer
> may not have started yet or is in progress. Thus if the reader
> of the residue accesses the DMA buffer too quickly, the CPU is
> misinformed about the data that has been successfully processed.
> 
> The CCSTAT.ACTV register is a boolean that is set if any TR is
> being processed by either the EMDA3CC or EDMA3TC. By polling
> this register it is possible to ensure that the residue value
> returned is valid for immediate processing. However, since the
> DMA engine may be active, polling may never hit a moment where
> no TR is being processed. To handle this, the SRC/DST is also
> polled to see if it changes. And as a last resort, a max loop
> count for the busy waiting exists to avoid an infinite loop.
> 
> Signed-off-by: John Ogness <[email-masked]>
> ---
>  v1-v2 changes
>  . rebased for next-20151016
>  . added multiple exit conditions for busy wait loop
> 
>  v2-v3 changes
>  . rebased for next-20160121
>  . reduced max loops from 10000 to 1000
>  . loop countdown instead of count
>  . change debug print function and message
>  . fine-tune comments
> 
>  drivers/dma/edma.c |   40 +++++++++++++++++++++++++++++++++++++++-
>  1 file changed, 39 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/dma/edma.c b/drivers/dma/edma.c
> index d92d655..20cf643 100644
> --- a/drivers/dma/edma.c
> +++ b/drivers/dma/edma.c
> @@ -1680,9 +1680,22 @@ static void edma_issue_pending(struct dma_chan *chan)
>  	spin_unlock_irqrestore(&echan->vchan.lock, flags);
>  }
>  
> +#define EDMA_CCSTAT_ACTV (1 << 4)

Can you move this to the define section of the file on top?

> +
> +/*
> + * This limit exists to avoid a possible infinite loop when waiting for proof
> + * that a particular transfer is completed. This limit can be hit if there
> + * are large bursts to/from slow devices or the CPU is never able to catch
> + * the DMA hardware idle. On an AM335x transfering 48 bytes from the UART
> + * RX-FIFO, as many as 55 loops have been seen.
> + */
> +#define EDMA_MAX_TR_WAIT_LOOPS 1000
> +
>  static u32 edma_residue(struct edma_desc *edesc)
>  {
>  	bool dst = edesc->direction == DMA_DEV_TO_MEM;
> +	int loop_count = EDMA_MAX_TR_WAIT_LOOPS;
> +	struct edma_chan *echan = edesc->echan;
>  	struct edma_pset *pset = edesc->pset;
>  	dma_addr_t done, pos;
>  	int i;
> @@ -1691,7 +1704,32 @@ static u32 edma_residue(struct edma_desc *edesc)
>  	 * We always read the dst/src position from the first RamPar
>  	 * pset. That's the one which is active now.
>  	 */
> -	pos = edma_get_position(edesc->echan->ecc, edesc->echan->slot[0], dst);
> +	pos = edma_get_position(echan->ecc, echan->slot[0], dst);
> +
> +	/*
> +	 * "pos" may represent a transfer request that is still being
> +	 * processed by the EDMACC or EDMATC. We will busy wait until
> +	 * any one of the situations occurs:
> +	 *   1. the DMA hardware is idle
> +	 *   2. a new transfer request is setup
> +	 *   3. we hit the loop limit
> +	 */
> +	while (edma_read(echan->ecc, EDMA_CCSTAT) & EDMA_CCSTAT_ACTV) {
> +		/* check if a new transfer request is setup */
> +		if (edma_get_position(echan->ecc,
> +				      echan->slot[0], dst) != pos) {
> +			break;
> +		}
> +
> +		if (!--loop_count) {
> +			dev_dbg_ratelimited(echan->vchan.chan.device->dev,
> +				"%s: timeout waiting for PaRAM update\n",
> +				__func__);
> +			break;
> +		}
> +
> +		cpu_relax();
> +	}
>  
>  	/*
>  	 * Cyclic is simple. Just subtract pset[0].addr from pos.
> 

-- 
P?ter