stable.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 1/2] dmaengine: ep93xx: Always start from BASE0
       [not found] <20170513221327.10114-1-alexander.sverdlin@gmail.com>
@ 2017-05-13 22:13 ` Alexander Sverdlin
  2017-05-14 13:14   ` Vinod Koul
  2017-05-13 22:13 ` [PATCH 2/2] dmaengine: ep93xx: Don't drain the transfers in terminate_all() Alexander Sverdlin
  1 sibling, 1 reply; 5+ messages in thread
From: Alexander Sverdlin @ 2017-05-13 22:13 UTC (permalink / raw)
  To: dmaengine; +Cc: Alexander Sverdlin, Dan Williams, Vinod Koul, stable

The current buffer is being reset to zero on device_free_chan_resources()
but not on device_terminate_all(). It could happen that HW is restarted and
expects BASE0 to be used, but the driver is not synchronized and will start
from BASE1. Therefore, it's better to reset the buffer explicitly in
m2p_hw_setup(). While here, check that HW really starts from BASE0 and
warn otherwise.

Signed-off-by: Alexander Sverdlin <alexander.sverdlin@gmail.com>
Cc: stable@vger.kernel.org
---
 drivers/dma/ep93xx_dma.c | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/drivers/dma/ep93xx_dma.c b/drivers/dma/ep93xx_dma.c
index d37e8dda8079..a3d946d2a8e1 100644
--- a/drivers/dma/ep93xx_dma.c
+++ b/drivers/dma/ep93xx_dma.c
@@ -45,6 +45,7 @@
 
 #define M2P_PPALLOC			0x0008
 #define M2P_STATUS			0x000c
+#define M2P_STATUS_NEXTBUFFER		BIT(6)
 
 #define M2P_MAXCNT0			0x0020
 #define M2P_BASE0			0x0024
@@ -312,6 +313,13 @@ static void m2p_set_control(struct ep93xx_dma_chan *edmac, u32 control)
 	readl(edmac->regs + M2P_CONTROL);
 }
 
+static inline u32 m2p_channel_nextbuf(struct ep93xx_dma_chan *edmac)
+{
+	if (readl(edmac->regs + M2P_STATUS) & M2P_STATUS_NEXTBUFFER)
+		return 1;
+	return 0;
+}
+
 static int m2p_hw_setup(struct ep93xx_dma_chan *edmac)
 {
 	struct ep93xx_dma_data *data = edmac->chan.private;
@@ -323,6 +331,10 @@ static int m2p_hw_setup(struct ep93xx_dma_chan *edmac)
 		| M2P_CONTROL_ENABLE;
 	m2p_set_control(edmac, control);
 
+	if (m2p_channel_nextbuf(edmac) != 0)
+		dev_warn(chan2dev(edmac), "M2P: Starting from BASE1\n");
+	edmac->buffer = 0;
+
 	return 0;
 }
 
-- 
2.12.2

^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH 2/2] dmaengine: ep93xx: Don't drain the transfers in terminate_all()
       [not found] <20170513221327.10114-1-alexander.sverdlin@gmail.com>
  2017-05-13 22:13 ` [PATCH 1/2] dmaengine: ep93xx: Always start from BASE0 Alexander Sverdlin
@ 2017-05-13 22:13 ` Alexander Sverdlin
  2017-05-14 13:16   ` Vinod Koul
  1 sibling, 1 reply; 5+ messages in thread
From: Alexander Sverdlin @ 2017-05-13 22:13 UTC (permalink / raw)
  To: dmaengine; +Cc: Alexander Sverdlin, Dan Williams, Vinod Koul, stable

As dmaengine.h suggests, device_terminate_all() "Aborts all transfers on a
channel". Seems that no other driver tried to busy-wait and actually drain
the data. Moreover, this happens with IRQs disabled, therefore induces huge
latency:

 irqsoff latency trace v1.1.5 on 4.11.0
 --------------------------------------------------------------------
 latency: 39770 us, #57/57, CPU#0 | (M:preempt VP:0, KP:0, SP:0 HP:0)
    -----------------
    | task: process-129 (uid:0 nice:0 policy:2 rt_prio:50)
    -----------------
  => started at: _snd_pcm_stream_lock_irqsave
  => ended at:   snd_pcm_stream_unlock_irqrestore

                  _------=> CPU#
                 / _-----=> irqs-off
                | / _----=> need-resched
                || / _---=> hardirq/softirq
                ||| / _--=> preempt-depth
                |||| /     delay
  cmd     pid   ||||| time  |   caller
     \   /      |||||  \    |   /
process-129     0d.s.    3us : _snd_pcm_stream_lock_irqsave
process-129     0d.s1    9us : snd_pcm_stream_lock <-_snd_pcm_stream_lock_irqsave
process-129     0d.s1   15us : preempt_count_add <-snd_pcm_stream_lock
process-129     0d.s2   22us : preempt_count_add <-snd_pcm_stream_lock
process-129     0d.s3   32us : snd_pcm_update_hw_ptr0 <-snd_pcm_period_elapsed
process-129     0d.s3   41us : soc_pcm_pointer <-snd_pcm_update_hw_ptr0
process-129     0d.s3   50us : dmaengine_pcm_pointer <-soc_pcm_pointer
process-129     0d.s3   58us+: snd_dmaengine_pcm_pointer_no_residue <-dmaengine_pcm_pointer
process-129     0d.s3   96us : update_audio_tstamp <-snd_pcm_update_hw_ptr0
process-129     0d.s3  103us : snd_pcm_update_state <-snd_pcm_update_hw_ptr0
process-129     0d.s3  112us : xrun <-snd_pcm_update_state
process-129     0d.s3  119us : snd_pcm_stop <-xrun
process-129     0d.s3  126us : snd_pcm_action <-snd_pcm_stop
process-129     0d.s3  134us : snd_pcm_action_single <-snd_pcm_action
process-129     0d.s3  141us : snd_pcm_pre_stop <-snd_pcm_action_single
process-129     0d.s3  150us : snd_pcm_do_stop <-snd_pcm_action_single
process-129     0d.s3  157us : soc_pcm_trigger <-snd_pcm_do_stop
process-129     0d.s3  166us : snd_dmaengine_pcm_trigger <-soc_pcm_trigger
process-129     0d.s3  175us : ep93xx_dma_terminate_all <-snd_dmaengine_pcm_trigger
process-129     0d.s3  182us : preempt_count_add <-ep93xx_dma_terminate_all
process-129     0d.s4  189us*: m2p_hw_shutdown <-ep93xx_dma_terminate_all
process-129     0d.s4 39472us : m2p_hw_setup <-ep93xx_dma_terminate_all

 ... rest skipped...

process-129     0d.s. 40080us : <stack trace>
 => ep93xx_dma_tasklet
 => tasklet_action
 => __do_softirq
 => irq_exit
 => __handle_domain_irq
 => vic_handle_irq
 => __irq_usr
 => 0xb66c6668

Just abort the transfers and warn if the HW state is not what we expect.

Signed-off-by: Alexander Sverdlin <alexander.sverdlin@gmail.com>
Cc: stable@vger.kernel.org
---
 drivers/dma/ep93xx_dma.c | 13 ++-----------
 1 file changed, 2 insertions(+), 11 deletions(-)

diff --git a/drivers/dma/ep93xx_dma.c b/drivers/dma/ep93xx_dma.c
index a3d946d2a8e1..e78d1fbf2179 100644
--- a/drivers/dma/ep93xx_dma.c
+++ b/drivers/dma/ep93xx_dma.c
@@ -345,19 +345,10 @@ static inline u32 m2p_channel_state(struct ep93xx_dma_chan *edmac)
 
 static void m2p_hw_shutdown(struct ep93xx_dma_chan *edmac)
 {
-	u32 control;
-
-	control = readl(edmac->regs + M2P_CONTROL);
-	control &= ~(M2P_CONTROL_STALLINT | M2P_CONTROL_NFBINT);
-	m2p_set_control(edmac, control);
-
-	while (m2p_channel_state(edmac) >= M2P_STATE_ON)
-		cpu_relax();
-
 	m2p_set_control(edmac, 0);
 
-	while (m2p_channel_state(edmac) == M2P_STATE_STALL)
-		cpu_relax();
+	while (m2p_channel_state(edmac) != M2P_STATE_IDLE)
+		dev_warn(chan2dev(edmac), "M2P: Not yet IDLE\n");
 }
 
 static void m2p_fill_desc(struct ep93xx_dma_chan *edmac)
-- 
2.12.2

^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [PATCH 1/2] dmaengine: ep93xx: Always start from BASE0
  2017-05-13 22:13 ` [PATCH 1/2] dmaengine: ep93xx: Always start from BASE0 Alexander Sverdlin
@ 2017-05-14 13:14   ` Vinod Koul
  2017-05-14 13:33     ` Alexander Sverdlin
  0 siblings, 1 reply; 5+ messages in thread
From: Vinod Koul @ 2017-05-14 13:14 UTC (permalink / raw)
  To: Alexander Sverdlin; +Cc: dmaengine, Dan Williams, stable

On Sun, May 14, 2017 at 12:13:26AM +0200, Alexander Sverdlin wrote:
> The current buffer is being reset to zero on device_free_chan_resources()
> but not on device_terminate_all(). It could happen that HW is restarted and
> expects BASE0 to be used, but the driver is not synchronized and will start
> from BASE1. Therefore, it's better to reset the buffer explicitly in
> m2p_hw_setup(). While here, check that HW really starts from BASE0 and
> warn otherwise.
> 
> Signed-off-by: Alexander Sverdlin <alexander.sverdlin@gmail.com>
> Cc: stable@vger.kernel.org
> ---
>  drivers/dma/ep93xx_dma.c | 12 ++++++++++++
>  1 file changed, 12 insertions(+)
> 
> diff --git a/drivers/dma/ep93xx_dma.c b/drivers/dma/ep93xx_dma.c
> index d37e8dda8079..a3d946d2a8e1 100644
> --- a/drivers/dma/ep93xx_dma.c
> +++ b/drivers/dma/ep93xx_dma.c
> @@ -45,6 +45,7 @@
>  
>  #define M2P_PPALLOC			0x0008
>  #define M2P_STATUS			0x000c
> +#define M2P_STATUS_NEXTBUFFER		BIT(6)
>  
>  #define M2P_MAXCNT0			0x0020
>  #define M2P_BASE0			0x0024
> @@ -312,6 +313,13 @@ static void m2p_set_control(struct ep93xx_dma_chan *edmac, u32 control)
>  	readl(edmac->regs + M2P_CONTROL);
>  }
>  
> +static inline u32 m2p_channel_nextbuf(struct ep93xx_dma_chan *edmac)
> +{
> +	if (readl(edmac->regs + M2P_STATUS) & M2P_STATUS_NEXTBUFFER)
> +		return 1;
> +	return 0;
> +}
> +
>  static int m2p_hw_setup(struct ep93xx_dma_chan *edmac)
>  {
>  	struct ep93xx_dma_data *data = edmac->chan.private;
> @@ -323,6 +331,10 @@ static int m2p_hw_setup(struct ep93xx_dma_chan *edmac)
>  		| M2P_CONTROL_ENABLE;
>  	m2p_set_control(edmac, control);
>  
> +	if (m2p_channel_nextbuf(edmac) != 0)
> +		dev_warn(chan2dev(edmac), "M2P: Starting from BASE1\n");

But then you are actually not restarting from BASE1 as you reset, so the
warn is wrong.. Perhaps a more meaningful msg would be to say "expected 0
but found 1, so resetting"

> +	edmac->buffer = 0;
> +
>  	return 0;
>  }
>  
> -- 
> 2.12.2
> 

-- 
~Vinod

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH 2/2] dmaengine: ep93xx: Don't drain the transfers in terminate_all()
  2017-05-13 22:13 ` [PATCH 2/2] dmaengine: ep93xx: Don't drain the transfers in terminate_all() Alexander Sverdlin
@ 2017-05-14 13:16   ` Vinod Koul
  0 siblings, 0 replies; 5+ messages in thread
From: Vinod Koul @ 2017-05-14 13:16 UTC (permalink / raw)
  To: Alexander Sverdlin; +Cc: dmaengine, Dan Williams, stable

On Sun, May 14, 2017 at 12:13:27AM +0200, Alexander Sverdlin wrote:
> As dmaengine.h suggests, device_terminate_all() "Aborts all transfers on a
> channel". Seems that no other driver tried to busy-wait and actually drain
> the data. Moreover, this happens with IRQs disabled, therefore induces huge
> latency:

Perhaps you should move current code to device_synchronize callback to allow
people to do so, if desired.

> 
>  irqsoff latency trace v1.1.5 on 4.11.0
>  --------------------------------------------------------------------
>  latency: 39770 us, #57/57, CPU#0 | (M:preempt VP:0, KP:0, SP:0 HP:0)
>     -----------------
>     | task: process-129 (uid:0 nice:0 policy:2 rt_prio:50)
>     -----------------
>   => started at: _snd_pcm_stream_lock_irqsave
>   => ended at:   snd_pcm_stream_unlock_irqrestore
> 
>                   _------=> CPU#
>                  / _-----=> irqs-off
>                 | / _----=> need-resched
>                 || / _---=> hardirq/softirq
>                 ||| / _--=> preempt-depth
>                 |||| /     delay
>   cmd     pid   ||||| time  |   caller
>      \   /      |||||  \    |   /
> process-129     0d.s.    3us : _snd_pcm_stream_lock_irqsave
> process-129     0d.s1    9us : snd_pcm_stream_lock <-_snd_pcm_stream_lock_irqsave
> process-129     0d.s1   15us : preempt_count_add <-snd_pcm_stream_lock
> process-129     0d.s2   22us : preempt_count_add <-snd_pcm_stream_lock
> process-129     0d.s3   32us : snd_pcm_update_hw_ptr0 <-snd_pcm_period_elapsed
> process-129     0d.s3   41us : soc_pcm_pointer <-snd_pcm_update_hw_ptr0
> process-129     0d.s3   50us : dmaengine_pcm_pointer <-soc_pcm_pointer
> process-129     0d.s3   58us+: snd_dmaengine_pcm_pointer_no_residue <-dmaengine_pcm_pointer
> process-129     0d.s3   96us : update_audio_tstamp <-snd_pcm_update_hw_ptr0
> process-129     0d.s3  103us : snd_pcm_update_state <-snd_pcm_update_hw_ptr0
> process-129     0d.s3  112us : xrun <-snd_pcm_update_state
> process-129     0d.s3  119us : snd_pcm_stop <-xrun
> process-129     0d.s3  126us : snd_pcm_action <-snd_pcm_stop
> process-129     0d.s3  134us : snd_pcm_action_single <-snd_pcm_action
> process-129     0d.s3  141us : snd_pcm_pre_stop <-snd_pcm_action_single
> process-129     0d.s3  150us : snd_pcm_do_stop <-snd_pcm_action_single
> process-129     0d.s3  157us : soc_pcm_trigger <-snd_pcm_do_stop
> process-129     0d.s3  166us : snd_dmaengine_pcm_trigger <-soc_pcm_trigger
> process-129     0d.s3  175us : ep93xx_dma_terminate_all <-snd_dmaengine_pcm_trigger
> process-129     0d.s3  182us : preempt_count_add <-ep93xx_dma_terminate_all
> process-129     0d.s4  189us*: m2p_hw_shutdown <-ep93xx_dma_terminate_all
> process-129     0d.s4 39472us : m2p_hw_setup <-ep93xx_dma_terminate_all
> 
>  ... rest skipped...
> 
> process-129     0d.s. 40080us : <stack trace>
>  => ep93xx_dma_tasklet
>  => tasklet_action
>  => __do_softirq
>  => irq_exit
>  => __handle_domain_irq
>  => vic_handle_irq
>  => __irq_usr
>  => 0xb66c6668
> 
> Just abort the transfers and warn if the HW state is not what we expect.
> 
> Signed-off-by: Alexander Sverdlin <alexander.sverdlin@gmail.com>
> Cc: stable@vger.kernel.org
> ---
>  drivers/dma/ep93xx_dma.c | 13 ++-----------
>  1 file changed, 2 insertions(+), 11 deletions(-)
> 
> diff --git a/drivers/dma/ep93xx_dma.c b/drivers/dma/ep93xx_dma.c
> index a3d946d2a8e1..e78d1fbf2179 100644
> --- a/drivers/dma/ep93xx_dma.c
> +++ b/drivers/dma/ep93xx_dma.c
> @@ -345,19 +345,10 @@ static inline u32 m2p_channel_state(struct ep93xx_dma_chan *edmac)
>  
>  static void m2p_hw_shutdown(struct ep93xx_dma_chan *edmac)
>  {
> -	u32 control;
> -
> -	control = readl(edmac->regs + M2P_CONTROL);
> -	control &= ~(M2P_CONTROL_STALLINT | M2P_CONTROL_NFBINT);
> -	m2p_set_control(edmac, control);
> -
> -	while (m2p_channel_state(edmac) >= M2P_STATE_ON)
> -		cpu_relax();
> -
>  	m2p_set_control(edmac, 0);
>  
> -	while (m2p_channel_state(edmac) == M2P_STATE_STALL)
> -		cpu_relax();
> +	while (m2p_channel_state(edmac) != M2P_STATE_IDLE)
> +		dev_warn(chan2dev(edmac), "M2P: Not yet IDLE\n");
>  }
>  
>  static void m2p_fill_desc(struct ep93xx_dma_chan *edmac)
> -- 
> 2.12.2
> 

-- 
~Vinod

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH 1/2] dmaengine: ep93xx: Always start from BASE0
  2017-05-14 13:14   ` Vinod Koul
@ 2017-05-14 13:33     ` Alexander Sverdlin
  0 siblings, 0 replies; 5+ messages in thread
From: Alexander Sverdlin @ 2017-05-14 13:33 UTC (permalink / raw)
  To: Vinod Koul; +Cc: dmaengine, Dan Williams, stable

Hello Vinod,

On 14/05/17 15:14, Vinod Koul wrote:
>> @@ -323,6 +331,10 @@ static int m2p_hw_setup(struct ep93xx_dma_chan *edmac)
>>  		| M2P_CONTROL_ENABLE;
>>  	m2p_set_control(edmac, control);
>>  
>> +	if (m2p_channel_nextbuf(edmac) != 0)
>> +		dev_warn(chan2dev(edmac), "M2P: Starting from BASE1\n");
> But then you are actually not restarting from BASE1 as you reset, so the
> warn is wrong.. Perhaps a more meaningful msg would be to say "expected 0
> but found 1, so resetting"

This is, maybe not so obvious, a warning, which should never fire. It just
proves, that I understand the HW correctly. As long as nobody sees the warning,
it's the case. And it proves that HW behaves according to user manual.
We can remove this warning completely.

Once again, the story is following:

- m2p_hw_setup() is always called after channel reset
- after reset channel expects to start from buffer 0
- therefore we must always start from buffer 0 <= this was not the case before
- we can read the expected "next buffer" from the controller => hence the warning
  maybe if there will be a bug somewhere and the channel will not be reset before
  next transfer, it will be triggered

Regards,
Alexander.

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2017-05-14 13:33 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
     [not found] <20170513221327.10114-1-alexander.sverdlin@gmail.com>
2017-05-13 22:13 ` [PATCH 1/2] dmaengine: ep93xx: Always start from BASE0 Alexander Sverdlin
2017-05-14 13:14   ` Vinod Koul
2017-05-14 13:33     ` Alexander Sverdlin
2017-05-13 22:13 ` [PATCH 2/2] dmaengine: ep93xx: Don't drain the transfers in terminate_all() Alexander Sverdlin
2017-05-14 13:16   ` Vinod Koul

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).