All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] ALSA: compress: fix drain calls blocking other compress functions
@ 2013-10-23 16:17 Vinod Koul
  2013-10-24  7:35 ` Vinod Koul
  0 siblings, 1 reply; 19+ messages in thread
From: Vinod Koul @ 2013-10-23 16:17 UTC (permalink / raw)
  To: alsa-devel; +Cc: tiwai, Vinod Koul, broonie, lgirdwood

The drain and drain_notify callback were blocked by low level driver untill the
draining was complete. Due to this being invoked with big fat mutex held, others
ops like reading timestamp, calling pause, drop were blocked.

So to fix this we add a new snd_compr_drain_notify() API. This would be required
to be invoked by low level driver when drain or partial drain has been completed
by the DSP. Thus we make the drain and partial_drain callback as non blocking
and driver returns immediately after notifying DSP.  The waiting is done while
relasing the lock so that other ops can go ahead.

Signed-off-by: Vinod Koul <vinod.koul@intel.com>
CC: stable@vger.kernel.org
---
 include/sound/compress_driver.h |   12 ++++++++++
 sound/core/compress_offload.c   |   43 +++++++++++++++++++++++++++++++++++---
 2 files changed, 51 insertions(+), 4 deletions(-)

diff --git a/include/sound/compress_driver.h b/include/sound/compress_driver.h
index 9031a26..eff5f84 100644
--- a/include/sound/compress_driver.h
+++ b/include/sound/compress_driver.h
@@ -48,6 +48,8 @@ struct snd_compr_ops;
  *	the ring buffer
  * @total_bytes_transferred: cumulative bytes transferred by offload DSP
  * @sleep: poll sleep
+ * @wait: drain wait queuq
+ * @draining: draining wait condition
  */
 struct snd_compr_runtime {
 	snd_pcm_state_t state;
@@ -59,6 +61,8 @@ struct snd_compr_runtime {
 	u64 total_bytes_available;
 	u64 total_bytes_transferred;
 	wait_queue_head_t sleep;
+	wait_queue_head_t wait;
+	unsigned int draining;
 	void *private_data;
 };
 
@@ -171,4 +175,12 @@ static inline void snd_compr_fragment_elapsed(struct snd_compr_stream *stream)
 	wake_up(&stream->runtime->sleep);
 }
 
+static inline void snd_compr_drain_notify(struct snd_compr_stream *stream)
+{
+	snd_BUG_ON(!stream);
+
+	stream->runtime->draining = 1;
+	wake_up(&stream->runtime->wait);
+}
+
 #endif
diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c
index bea523a..3582f9f 100644
--- a/sound/core/compress_offload.c
+++ b/sound/core/compress_offload.c
@@ -123,6 +123,7 @@ static int snd_compr_open(struct inode *inode, struct file *f)
 	}
 	runtime->state = SNDRV_PCM_STATE_OPEN;
 	init_waitqueue_head(&runtime->sleep);
+	init_waitqueue_head(&runtime->wait);
 	data->stream.runtime = runtime;
 	f->private_data = (void *)data;
 	mutex_lock(&compr->lock);
@@ -682,12 +683,36 @@ static int snd_compr_stop(struct snd_compr_stream *stream)
 	if (!retval) {
 		stream->runtime->state = SNDRV_PCM_STATE_SETUP;
 		wake_up(&stream->runtime->sleep);
+		stream->runtime->draining = 1;
+		wake_up(&stream->runtime->wait);
 		stream->runtime->total_bytes_available = 0;
 		stream->runtime->total_bytes_transferred = 0;
 	}
 	return retval;
 }
 
+static int snd_compress_wait_for_drain(struct snd_compr_stream *stream)
+{
+	int retval = 0;
+
+	/*
+	 * we are called with lock held. So drop the lock while we wait for
+	 * drain complete notfication from the driver
+	 */
+	stream->runtime->state = SNDRV_PCM_STATE_DRAINING;
+	mutex_unlock(&stream->device->lock);
+
+	retval = wait_event_interruptible(stream->runtime->wait, stream->runtime->draining);
+	if (retval)
+		pr_err("Wait for drain failed %d\n", retval);
+
+	wake_up(&stream->runtime->sleep);
+	mutex_lock(&stream->device->lock);
+	stream->runtime->state = SNDRV_PCM_STATE_SETUP;
+
+	return retval;
+}
+
 static int snd_compr_drain(struct snd_compr_stream *stream)
 {
 	int retval;
@@ -695,12 +720,16 @@ static int snd_compr_drain(struct snd_compr_stream *stream)
 	if (stream->runtime->state == SNDRV_PCM_STATE_PREPARED ||
 			stream->runtime->state == SNDRV_PCM_STATE_SETUP)
 		return -EPERM;
+
+	stream->runtime->draining = 0;
 	retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_DRAIN);
-	if (!retval) {
-		stream->runtime->state = SNDRV_PCM_STATE_DRAINING;
+	if (retval) {
+		pr_err("SND_COMPR_TRIGGER_DRAIN failed %d\n", retval);
 		wake_up(&stream->runtime->sleep);
+		return retval;
 	}
-	return retval;
+
+	return snd_compress_wait_for_drain(stream);
 }
 
 static int snd_compr_next_track(struct snd_compr_stream *stream)
@@ -735,10 +764,16 @@ static int snd_compr_partial_drain(struct snd_compr_stream *stream)
 	if (stream->next_track == false)
 		return -EPERM;
 
+	stream->runtime->draining = 0;
 	retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_PARTIAL_DRAIN);
+	if (retval) {
+		pr_err("Partial drain returned failure\n");
+		wake_up(&stream->runtime->sleep);
+		return retval;
+	}
 
 	stream->next_track = false;
-	return retval;
+	return snd_compress_wait_for_drain(stream);
 }
 
 static long snd_compr_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
-- 
1.7.0.4

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

* [PATCH] ALSA: compress: fix drain calls blocking other compress functions
  2013-10-23 16:17 [PATCH] ALSA: compress: fix drain calls blocking other compress functions Vinod Koul
@ 2013-10-24  7:35 ` Vinod Koul
  2013-10-24  9:25   ` Takashi Iwai
  2013-10-24 11:07   ` [PATCH v3] " Vinod Koul
  0 siblings, 2 replies; 19+ messages in thread
From: Vinod Koul @ 2013-10-24  7:35 UTC (permalink / raw)
  To: alsa-devel; +Cc: tiwai, Vinod Koul, broonie, lgirdwood

The drain and drain_notify callback were blocked by low level driver untill the
draining was complete. Due to this being invoked with big fat mutex held, others
ops like reading timestamp, calling pause, drop were blocked.

So to fix this we add a new snd_compr_drain_notify() API. This would be required
to be invoked by low level driver when drain or partial drain has been completed
by the DSP. Thus we make the drain and partial_drain callback as non blocking
and driver returns immediately after notifying DSP.
The waiting is done while relasing the lock so that other ops can go ahead.

Signed-off-by: Vinod Koul <vinod.koul@intel.com>
CC: stable@vger.kernel.org
---
v2:
 fix the 80 line warn
 move the state change to compress_drain()

 include/sound/compress_driver.h |   12 ++++++++++
 sound/core/compress_offload.c   |   43 ++++++++++++++++++++++++++++++++++++--
 2 files changed, 52 insertions(+), 3 deletions(-)

diff --git a/include/sound/compress_driver.h b/include/sound/compress_driver.h
index 9031a26..eff5f84 100644
--- a/include/sound/compress_driver.h
+++ b/include/sound/compress_driver.h
@@ -48,6 +48,8 @@ struct snd_compr_ops;
  *	the ring buffer
  * @total_bytes_transferred: cumulative bytes transferred by offload DSP
  * @sleep: poll sleep
+ * @wait: drain wait queuq
+ * @draining: draining wait condition
  */
 struct snd_compr_runtime {
 	snd_pcm_state_t state;
@@ -59,6 +61,8 @@ struct snd_compr_runtime {
 	u64 total_bytes_available;
 	u64 total_bytes_transferred;
 	wait_queue_head_t sleep;
+	wait_queue_head_t wait;
+	unsigned int draining;
 	void *private_data;
 };
 
@@ -171,4 +175,12 @@ static inline void snd_compr_fragment_elapsed(struct snd_compr_stream *stream)
 	wake_up(&stream->runtime->sleep);
 }
 
+static inline void snd_compr_drain_notify(struct snd_compr_stream *stream)
+{
+	snd_BUG_ON(!stream);
+
+	stream->runtime->draining = 1;
+	wake_up(&stream->runtime->wait);
+}
+
 #endif
diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c
index bea523a..c030365 100644
--- a/sound/core/compress_offload.c
+++ b/sound/core/compress_offload.c
@@ -123,6 +123,7 @@ static int snd_compr_open(struct inode *inode, struct file *f)
 	}
 	runtime->state = SNDRV_PCM_STATE_OPEN;
 	init_waitqueue_head(&runtime->sleep);
+	init_waitqueue_head(&runtime->wait);
 	data->stream.runtime = runtime;
 	f->private_data = (void *)data;
 	mutex_lock(&compr->lock);
@@ -682,12 +683,36 @@ static int snd_compr_stop(struct snd_compr_stream *stream)
 	if (!retval) {
 		stream->runtime->state = SNDRV_PCM_STATE_SETUP;
 		wake_up(&stream->runtime->sleep);
+		stream->runtime->draining = 1;
+		wake_up(&stream->runtime->wait);
 		stream->runtime->total_bytes_available = 0;
 		stream->runtime->total_bytes_transferred = 0;
 	}
 	return retval;
 }
 
+static int snd_compress_wait_for_drain(struct snd_compr_stream *stream)
+{
+	int retval = 0;
+
+	/*
+	 * we are called with lock held. So drop the lock while we wait for
+	 * drain complete notfication from the driver
+	 */
+	stream->runtime->state = SNDRV_PCM_STATE_DRAINING;
+	mutex_unlock(&stream->device->lock);
+
+	retval = wait_event_interruptible(stream->runtime->wait,
+			stream->runtime->draining);
+	if (retval)
+		pr_err("Wait for drain failed %d\n", retval);
+
+	wake_up(&stream->runtime->sleep);
+	mutex_lock(&stream->device->lock);
+
+	return retval;
+}
+
 static int snd_compr_drain(struct snd_compr_stream *stream)
 {
 	int retval;
@@ -695,11 +720,17 @@ static int snd_compr_drain(struct snd_compr_stream *stream)
 	if (stream->runtime->state == SNDRV_PCM_STATE_PREPARED ||
 			stream->runtime->state == SNDRV_PCM_STATE_SETUP)
 		return -EPERM;
+
+	stream->runtime->draining = 0;
 	retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_DRAIN);
-	if (!retval) {
-		stream->runtime->state = SNDRV_PCM_STATE_DRAINING;
+	if (retval) {
+		pr_err("SND_COMPR_TRIGGER_DRAIN failed %d\n", retval);
 		wake_up(&stream->runtime->sleep);
+		return retval;
 	}
+
+	retval = snd_compress_wait_for_drain(stream);
+	stream->runtime->state = SNDRV_PCM_STATE_SETUP;
 	return retval;
 }
 
@@ -735,10 +766,16 @@ static int snd_compr_partial_drain(struct snd_compr_stream *stream)
 	if (stream->next_track == false)
 		return -EPERM;
 
+	stream->runtime->draining = 0;
 	retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_PARTIAL_DRAIN);
+	if (retval) {
+		pr_err("Partial drain returned failure\n");
+		wake_up(&stream->runtime->sleep);
+		return retval;
+	}
 
 	stream->next_track = false;
-	return retval;
+	return snd_compress_wait_for_drain(stream);
 }
 
 static long snd_compr_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
-- 
1.7.0.4

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

* Re: [PATCH] ALSA: compress: fix drain calls blocking other compress functions
  2013-10-24  7:35 ` Vinod Koul
@ 2013-10-24  9:25   ` Takashi Iwai
  2013-10-24  9:27     ` Vinod Koul
  2013-10-24 11:07   ` [PATCH v3] " Vinod Koul
  1 sibling, 1 reply; 19+ messages in thread
From: Takashi Iwai @ 2013-10-24  9:25 UTC (permalink / raw)
  To: Vinod Koul; +Cc: alsa-devel, broonie, lgirdwood

At Thu, 24 Oct 2013 13:05:41 +0530,
Vinod Koul wrote:
> 
> The drain and drain_notify callback were blocked by low level driver untill the
> draining was complete. Due to this being invoked with big fat mutex held, others
> ops like reading timestamp, calling pause, drop were blocked.
> 
> So to fix this we add a new snd_compr_drain_notify() API. This would be required
> to be invoked by low level driver when drain or partial drain has been completed
> by the DSP. Thus we make the drain and partial_drain callback as non blocking
> and driver returns immediately after notifying DSP.
> The waiting is done while relasing the lock so that other ops can go ahead.
> 
> Signed-off-by: Vinod Koul <vinod.koul@intel.com>
> CC: stable@vger.kernel.org
> ---
> v2:
>  fix the 80 line warn
>  move the state change to compress_drain()
> 
>  include/sound/compress_driver.h |   12 ++++++++++
>  sound/core/compress_offload.c   |   43 ++++++++++++++++++++++++++++++++++++--
>  2 files changed, 52 insertions(+), 3 deletions(-)
> 
> diff --git a/include/sound/compress_driver.h b/include/sound/compress_driver.h
> index 9031a26..eff5f84 100644
> --- a/include/sound/compress_driver.h
> +++ b/include/sound/compress_driver.h
> @@ -48,6 +48,8 @@ struct snd_compr_ops;
>   *	the ring buffer
>   * @total_bytes_transferred: cumulative bytes transferred by offload DSP
>   * @sleep: poll sleep
> + * @wait: drain wait queuq
> + * @draining: draining wait condition

What does this mean exactly?  I thought draining = 1 meaning that the
stream is draining (i.e. waiting for the finish notification), but
it looks opposite in your code below...


>   */
>  struct snd_compr_runtime {
>  	snd_pcm_state_t state;
> @@ -59,6 +61,8 @@ struct snd_compr_runtime {
>  	u64 total_bytes_available;
>  	u64 total_bytes_transferred;
>  	wait_queue_head_t sleep;
> +	wait_queue_head_t wait;
> +	unsigned int draining;
>  	void *private_data;
>  };
>  
> @@ -171,4 +175,12 @@ static inline void snd_compr_fragment_elapsed(struct snd_compr_stream *stream)
>  	wake_up(&stream->runtime->sleep);
>  }
>  
> +static inline void snd_compr_drain_notify(struct snd_compr_stream *stream)
> +{
> +	snd_BUG_ON(!stream);
> +
> +	stream->runtime->draining = 1;
> +	wake_up(&stream->runtime->wait);
> +}
> +
>  #endif
> diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c
> index bea523a..c030365 100644
> --- a/sound/core/compress_offload.c
> +++ b/sound/core/compress_offload.c
> @@ -123,6 +123,7 @@ static int snd_compr_open(struct inode *inode, struct file *f)
>  	}
>  	runtime->state = SNDRV_PCM_STATE_OPEN;
>  	init_waitqueue_head(&runtime->sleep);
> +	init_waitqueue_head(&runtime->wait);
>  	data->stream.runtime = runtime;
>  	f->private_data = (void *)data;
>  	mutex_lock(&compr->lock);
> @@ -682,12 +683,36 @@ static int snd_compr_stop(struct snd_compr_stream *stream)
>  	if (!retval) {
>  		stream->runtime->state = SNDRV_PCM_STATE_SETUP;
>  		wake_up(&stream->runtime->sleep);
> +		stream->runtime->draining = 1;
> +		wake_up(&stream->runtime->wait);

Not using snd_compr_drain_notify()?


>  		stream->runtime->total_bytes_available = 0;
>  		stream->runtime->total_bytes_transferred = 0;
>  	}
>  	return retval;
>  }
>  
> +static int snd_compress_wait_for_drain(struct snd_compr_stream *stream)
> +{
> +	int retval = 0;
> +
> +	/*
> +	 * we are called with lock held. So drop the lock while we wait for
> +	 * drain complete notfication from the driver
> +	 */
> +	stream->runtime->state = SNDRV_PCM_STATE_DRAINING;
> +	mutex_unlock(&stream->device->lock);
> +
> +	retval = wait_event_interruptible(stream->runtime->wait,
> +			stream->runtime->draining);
> +	if (retval)
> +		pr_err("Wait for drain failed %d\n", retval);

If you make it interruptible, it's no actual kernel error when user
interrupts the operation, so no reason to put the kernel error message
there.  At least, better to check whether it's a user interruption or
a system error.


> +
> +	wake_up(&stream->runtime->sleep);
> +	mutex_lock(&stream->device->lock);
> +
> +	return retval;
> +}
> +
>  static int snd_compr_drain(struct snd_compr_stream *stream)
>  {
>  	int retval;
> @@ -695,11 +720,17 @@ static int snd_compr_drain(struct snd_compr_stream *stream)
>  	if (stream->runtime->state == SNDRV_PCM_STATE_PREPARED ||
>  			stream->runtime->state == SNDRV_PCM_STATE_SETUP)
>  		return -EPERM;
> +
> +	stream->runtime->draining = 0;
>  	retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_DRAIN);
> -	if (!retval) {
> -		stream->runtime->state = SNDRV_PCM_STATE_DRAINING;
> +	if (retval) {
> +		pr_err("SND_COMPR_TRIGGER_DRAIN failed %d\n", retval);
>  		wake_up(&stream->runtime->sleep);
> +		return retval;
>  	}
> +
> +	retval = snd_compress_wait_for_drain(stream);
> +	stream->runtime->state = SNDRV_PCM_STATE_SETUP;

So, the runtime state goes to SETUP even if wait_for_drain() returns
an error?  In other words, compress core expects that the driver takes
care of the proper state change in the error handling?


thanks,

Takashi

>  	return retval;
>  }
>  
> @@ -735,10 +766,16 @@ static int snd_compr_partial_drain(struct snd_compr_stream *stream)
>  	if (stream->next_track == false)
>  		return -EPERM;
>  
> +	stream->runtime->draining = 0;
>  	retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_PARTIAL_DRAIN);
> +	if (retval) {
> +		pr_err("Partial drain returned failure\n");
> +		wake_up(&stream->runtime->sleep);
> +		return retval;
> +	}
>  
>  	stream->next_track = false;
> -	return retval;
> +	return snd_compress_wait_for_drain(stream);
>  }
>  
>  static long snd_compr_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
> -- 
> 1.7.0.4
> 

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

* Re: [PATCH] ALSA: compress: fix drain calls blocking other compress functions
  2013-10-24  9:25   ` Takashi Iwai
@ 2013-10-24  9:27     ` Vinod Koul
  2013-10-24 10:59       ` Takashi Iwai
  0 siblings, 1 reply; 19+ messages in thread
From: Vinod Koul @ 2013-10-24  9:27 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: alsa-devel, broonie, lgirdwood

On Thu, Oct 24, 2013 at 11:25:35AM +0200, Takashi Iwai wrote:
> At Thu, 24 Oct 2013 13:05:41 +0530,
> Vinod Koul wrote:
> > 
> > The drain and drain_notify callback were blocked by low level driver untill the
> > draining was complete. Due to this being invoked with big fat mutex held, others
> > ops like reading timestamp, calling pause, drop were blocked.
> > 
> > So to fix this we add a new snd_compr_drain_notify() API. This would be required
> > to be invoked by low level driver when drain or partial drain has been completed
> > by the DSP. Thus we make the drain and partial_drain callback as non blocking
> > and driver returns immediately after notifying DSP.
> > The waiting is done while relasing the lock so that other ops can go ahead.
> > 
> > Signed-off-by: Vinod Koul <vinod.koul@intel.com>
> > CC: stable@vger.kernel.org
> > ---
> > v2:
> >  fix the 80 line warn
> >  move the state change to compress_drain()
> > 
> >  include/sound/compress_driver.h |   12 ++++++++++
> >  sound/core/compress_offload.c   |   43 ++++++++++++++++++++++++++++++++++++--
> >  2 files changed, 52 insertions(+), 3 deletions(-)
> > 
> > diff --git a/include/sound/compress_driver.h b/include/sound/compress_driver.h
> > index 9031a26..eff5f84 100644
> > --- a/include/sound/compress_driver.h
> > +++ b/include/sound/compress_driver.h
> > @@ -48,6 +48,8 @@ struct snd_compr_ops;
> >   *	the ring buffer
> >   * @total_bytes_transferred: cumulative bytes transferred by offload DSP
> >   * @sleep: poll sleep
> > + * @wait: drain wait queuq
> > + * @draining: draining wait condition
> 
> What does this mean exactly?  I thought draining = 1 meaning that the
> stream is draining (i.e. waiting for the finish notification), but
> it looks opposite in your code below...
What i meant was the draining wait conditions which would be used for waiting.
Before wait the caller will set to 0 and on completion the callback will set to
1 and wake up.

> >   */
> >  struct snd_compr_runtime {
> >  	snd_pcm_state_t state;
> > @@ -59,6 +61,8 @@ struct snd_compr_runtime {
> >  	u64 total_bytes_available;
> >  	u64 total_bytes_transferred;
> >  	wait_queue_head_t sleep;
> > +	wait_queue_head_t wait;
> > +	unsigned int draining;
> >  	void *private_data;
> >  };
> >  
> > @@ -171,4 +175,12 @@ static inline void snd_compr_fragment_elapsed(struct snd_compr_stream *stream)
> >  	wake_up(&stream->runtime->sleep);
> >  }
> >  
> > +static inline void snd_compr_drain_notify(struct snd_compr_stream *stream)
> > +{
> > +	snd_BUG_ON(!stream);
> > +
> > +	stream->runtime->draining = 1;
> > +	wake_up(&stream->runtime->wait);
> > +}
> > +
> >  #endif
> > diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c
> > index bea523a..c030365 100644
> > --- a/sound/core/compress_offload.c
> > +++ b/sound/core/compress_offload.c
> > @@ -123,6 +123,7 @@ static int snd_compr_open(struct inode *inode, struct file *f)
> >  	}
> >  	runtime->state = SNDRV_PCM_STATE_OPEN;
> >  	init_waitqueue_head(&runtime->sleep);
> > +	init_waitqueue_head(&runtime->wait);
> >  	data->stream.runtime = runtime;
> >  	f->private_data = (void *)data;
> >  	mutex_lock(&compr->lock);
> > @@ -682,12 +683,36 @@ static int snd_compr_stop(struct snd_compr_stream *stream)
> >  	if (!retval) {
> >  		stream->runtime->state = SNDRV_PCM_STATE_SETUP;
> >  		wake_up(&stream->runtime->sleep);
> > +		stream->runtime->draining = 1;
> > +		wake_up(&stream->runtime->wait);
> 
> Not using snd_compr_drain_notify()?
Yes that should be added, thanks for pointing!

> >  		stream->runtime->total_bytes_available = 0;
> >  		stream->runtime->total_bytes_transferred = 0;
> >  	}
> >  	return retval;
> >  }
> >  
> > +static int snd_compress_wait_for_drain(struct snd_compr_stream *stream)
> > +{
> > +	int retval = 0;
> > +
> > +	/*
> > +	 * we are called with lock held. So drop the lock while we wait for
> > +	 * drain complete notfication from the driver
> > +	 */
> > +	stream->runtime->state = SNDRV_PCM_STATE_DRAINING;
> > +	mutex_unlock(&stream->device->lock);
> > +
> > +	retval = wait_event_interruptible(stream->runtime->wait,
> > +			stream->runtime->draining);
> > +	if (retval)
> > +		pr_err("Wait for drain failed %d\n", retval);
> 
> If you make it interruptible, it's no actual kernel error when user
> interrupts the operation, so no reason to put the kernel error message
> there.  At least, better to check whether it's a user interruption or
> a system error.
Agreed. I think I dont even need to make it interruptible. If user is
terminating the stop will be invoked and then with above fix it should work. We
can do away with interrutiple part :)

> > +
> > +	wake_up(&stream->runtime->sleep);
> > +	mutex_lock(&stream->device->lock);
> > +
> > +	return retval;
> > +}
> > +
> >  static int snd_compr_drain(struct snd_compr_stream *stream)
> >  {
> >  	int retval;
> > @@ -695,11 +720,17 @@ static int snd_compr_drain(struct snd_compr_stream *stream)
> >  	if (stream->runtime->state == SNDRV_PCM_STATE_PREPARED ||
> >  			stream->runtime->state == SNDRV_PCM_STATE_SETUP)
> >  		return -EPERM;
> > +
> > +	stream->runtime->draining = 0;
> >  	retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_DRAIN);
> > -	if (!retval) {
> > -		stream->runtime->state = SNDRV_PCM_STATE_DRAINING;
> > +	if (retval) {
> > +		pr_err("SND_COMPR_TRIGGER_DRAIN failed %d\n", retval);
> >  		wake_up(&stream->runtime->sleep);
> > +		return retval;
> >  	}
> > +
> > +	retval = snd_compress_wait_for_drain(stream);
> > +	stream->runtime->state = SNDRV_PCM_STATE_SETUP;
> 
> So, the runtime state goes to SETUP even if wait_for_drain() returns
> an error?  In other words, compress core expects that the driver takes
> care of the proper state change in the error handling?
I guess it would make sense to communicate error. I havent added notion of
status on drain complete. By adding another status flag in the callback we cna
let driver tell us if error occured. Then pass the error in draining value and
do the state change accordingly.

--
~Vinod

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

* Re: [PATCH] ALSA: compress: fix drain calls blocking other compress functions
  2013-10-24 10:59       ` Takashi Iwai
@ 2013-10-24 10:35         ` Vinod Koul
  2013-10-24 12:00           ` Takashi Iwai
  0 siblings, 1 reply; 19+ messages in thread
From: Vinod Koul @ 2013-10-24 10:35 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: alsa-devel, broonie, lgirdwood

On Thu, Oct 24, 2013 at 12:59:18PM +0200, Takashi Iwai wrote:
> At Thu, 24 Oct 2013 14:57:24 +0530,
> Vinod Koul wrote:
> > 
> > On Thu, Oct 24, 2013 at 11:25:35AM +0200, Takashi Iwai wrote:
> > > At Thu, 24 Oct 2013 13:05:41 +0530,
> > > Vinod Koul wrote:
> > > > 
> > > > The drain and drain_notify callback were blocked by low level driver untill the
> > > > draining was complete. Due to this being invoked with big fat mutex held, others
> > > > ops like reading timestamp, calling pause, drop were blocked.
> > > > 
> > > > So to fix this we add a new snd_compr_drain_notify() API. This would be required
> > > > to be invoked by low level driver when drain or partial drain has been completed
> > > > by the DSP. Thus we make the drain and partial_drain callback as non blocking
> > > > and driver returns immediately after notifying DSP.
> > > > The waiting is done while relasing the lock so that other ops can go ahead.
> > > > 
> > > > Signed-off-by: Vinod Koul <vinod.koul@intel.com>
> > > > CC: stable@vger.kernel.org
> > > > ---
> > > > v2:
> > > >  fix the 80 line warn
> > > >  move the state change to compress_drain()
> > > > 
> > > >  include/sound/compress_driver.h |   12 ++++++++++
> > > >  sound/core/compress_offload.c   |   43 ++++++++++++++++++++++++++++++++++++--
> > > >  2 files changed, 52 insertions(+), 3 deletions(-)
> > > > 
> > > > diff --git a/include/sound/compress_driver.h b/include/sound/compress_driver.h
> > > > index 9031a26..eff5f84 100644
> > > > --- a/include/sound/compress_driver.h
> > > > +++ b/include/sound/compress_driver.h
> > > > @@ -48,6 +48,8 @@ struct snd_compr_ops;
> > > >   *	the ring buffer
> > > >   * @total_bytes_transferred: cumulative bytes transferred by offload DSP
> > > >   * @sleep: poll sleep
> > > > + * @wait: drain wait queuq
> > > > + * @draining: draining wait condition
> > > 
> > > What does this mean exactly?  I thought draining = 1 meaning that the
> > > stream is draining (i.e. waiting for the finish notification), but
> > > it looks opposite in your code below...
> > What i meant was the draining wait conditions which would be used for waiting.
> > Before wait the caller will set to 0 and on completion the callback will set to
> > 1 and wake up.
> 
> Well, it's not so intuitive as the flag being named "draining".
> Please give a more detailed comment (or name it a bit less-confusing
> one :)
hmmm, yes it needs a better name :)
perhaps drain_condition or drain_wake would be apt!
 
> > > > +
> > > > +	wake_up(&stream->runtime->sleep);
> > > > +	mutex_lock(&stream->device->lock);
> > > > +
> > > > +	return retval;
> > > > +}
> > > > +
> > > >  static int snd_compr_drain(struct snd_compr_stream *stream)
> > > >  {
> > > >  	int retval;
> > > > @@ -695,11 +720,17 @@ static int snd_compr_drain(struct snd_compr_stream *stream)
> > > >  	if (stream->runtime->state == SNDRV_PCM_STATE_PREPARED ||
> > > >  			stream->runtime->state == SNDRV_PCM_STATE_SETUP)
> > > >  		return -EPERM;
> > > > +
> > > > +	stream->runtime->draining = 0;
> > > >  	retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_DRAIN);
> > > > -	if (!retval) {
> > > > -		stream->runtime->state = SNDRV_PCM_STATE_DRAINING;
> > > > +	if (retval) {
> > > > +		pr_err("SND_COMPR_TRIGGER_DRAIN failed %d\n", retval);
> > > >  		wake_up(&stream->runtime->sleep);
> > > > +		return retval;
> > > >  	}
> > > > +
> > > > +	retval = snd_compress_wait_for_drain(stream);
> > > > +	stream->runtime->state = SNDRV_PCM_STATE_SETUP;
> > > 
> > > So, the runtime state goes to SETUP even if wait_for_drain() returns
> > > an error?  In other words, compress core expects that the driver takes
> > > care of the proper state change in the error handling?
> > I guess it would make sense to communicate error. I havent added notion of
> > status on drain complete. By adding another status flag in the callback we cna
> > let driver tell us if error occured. Then pass the error in draining value and
> > do the state change accordingly.
> 
> I think it's fine as in the current patch, I just wanted to make sure
> that this runtime state change is the designed behavior.  If so, it'd
> be better to document / comment clearly.
Sure, lets add that explictly!

-- 
~Vinod

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

* Re: [PATCH] ALSA: compress: fix drain calls blocking other compress functions
  2013-10-24  9:27     ` Vinod Koul
@ 2013-10-24 10:59       ` Takashi Iwai
  2013-10-24 10:35         ` Vinod Koul
  0 siblings, 1 reply; 19+ messages in thread
From: Takashi Iwai @ 2013-10-24 10:59 UTC (permalink / raw)
  To: Vinod Koul; +Cc: alsa-devel, broonie, lgirdwood

At Thu, 24 Oct 2013 14:57:24 +0530,
Vinod Koul wrote:
> 
> On Thu, Oct 24, 2013 at 11:25:35AM +0200, Takashi Iwai wrote:
> > At Thu, 24 Oct 2013 13:05:41 +0530,
> > Vinod Koul wrote:
> > > 
> > > The drain and drain_notify callback were blocked by low level driver untill the
> > > draining was complete. Due to this being invoked with big fat mutex held, others
> > > ops like reading timestamp, calling pause, drop were blocked.
> > > 
> > > So to fix this we add a new snd_compr_drain_notify() API. This would be required
> > > to be invoked by low level driver when drain or partial drain has been completed
> > > by the DSP. Thus we make the drain and partial_drain callback as non blocking
> > > and driver returns immediately after notifying DSP.
> > > The waiting is done while relasing the lock so that other ops can go ahead.
> > > 
> > > Signed-off-by: Vinod Koul <vinod.koul@intel.com>
> > > CC: stable@vger.kernel.org
> > > ---
> > > v2:
> > >  fix the 80 line warn
> > >  move the state change to compress_drain()
> > > 
> > >  include/sound/compress_driver.h |   12 ++++++++++
> > >  sound/core/compress_offload.c   |   43 ++++++++++++++++++++++++++++++++++++--
> > >  2 files changed, 52 insertions(+), 3 deletions(-)
> > > 
> > > diff --git a/include/sound/compress_driver.h b/include/sound/compress_driver.h
> > > index 9031a26..eff5f84 100644
> > > --- a/include/sound/compress_driver.h
> > > +++ b/include/sound/compress_driver.h
> > > @@ -48,6 +48,8 @@ struct snd_compr_ops;
> > >   *	the ring buffer
> > >   * @total_bytes_transferred: cumulative bytes transferred by offload DSP
> > >   * @sleep: poll sleep
> > > + * @wait: drain wait queuq
> > > + * @draining: draining wait condition
> > 
> > What does this mean exactly?  I thought draining = 1 meaning that the
> > stream is draining (i.e. waiting for the finish notification), but
> > it looks opposite in your code below...
> What i meant was the draining wait conditions which would be used for waiting.
> Before wait the caller will set to 0 and on completion the callback will set to
> 1 and wake up.

Well, it's not so intuitive as the flag being named "draining".
Please give a more detailed comment (or name it a bit less-confusing
one :)

> > >   */
> > >  struct snd_compr_runtime {
> > >  	snd_pcm_state_t state;
> > > @@ -59,6 +61,8 @@ struct snd_compr_runtime {
> > >  	u64 total_bytes_available;
> > >  	u64 total_bytes_transferred;
> > >  	wait_queue_head_t sleep;
> > > +	wait_queue_head_t wait;
> > > +	unsigned int draining;
> > >  	void *private_data;
> > >  };
> > >  
> > > @@ -171,4 +175,12 @@ static inline void snd_compr_fragment_elapsed(struct snd_compr_stream *stream)
> > >  	wake_up(&stream->runtime->sleep);
> > >  }
> > >  
> > > +static inline void snd_compr_drain_notify(struct snd_compr_stream *stream)
> > > +{
> > > +	snd_BUG_ON(!stream);
> > > +
> > > +	stream->runtime->draining = 1;
> > > +	wake_up(&stream->runtime->wait);
> > > +}
> > > +
> > >  #endif
> > > diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c
> > > index bea523a..c030365 100644
> > > --- a/sound/core/compress_offload.c
> > > +++ b/sound/core/compress_offload.c
> > > @@ -123,6 +123,7 @@ static int snd_compr_open(struct inode *inode, struct file *f)
> > >  	}
> > >  	runtime->state = SNDRV_PCM_STATE_OPEN;
> > >  	init_waitqueue_head(&runtime->sleep);
> > > +	init_waitqueue_head(&runtime->wait);
> > >  	data->stream.runtime = runtime;
> > >  	f->private_data = (void *)data;
> > >  	mutex_lock(&compr->lock);
> > > @@ -682,12 +683,36 @@ static int snd_compr_stop(struct snd_compr_stream *stream)
> > >  	if (!retval) {
> > >  		stream->runtime->state = SNDRV_PCM_STATE_SETUP;
> > >  		wake_up(&stream->runtime->sleep);
> > > +		stream->runtime->draining = 1;
> > > +		wake_up(&stream->runtime->wait);
> > 
> > Not using snd_compr_drain_notify()?
> Yes that should be added, thanks for pointing!
> 
> > >  		stream->runtime->total_bytes_available = 0;
> > >  		stream->runtime->total_bytes_transferred = 0;
> > >  	}
> > >  	return retval;
> > >  }
> > >  
> > > +static int snd_compress_wait_for_drain(struct snd_compr_stream *stream)
> > > +{
> > > +	int retval = 0;
> > > +
> > > +	/*
> > > +	 * we are called with lock held. So drop the lock while we wait for
> > > +	 * drain complete notfication from the driver
> > > +	 */
> > > +	stream->runtime->state = SNDRV_PCM_STATE_DRAINING;
> > > +	mutex_unlock(&stream->device->lock);
> > > +
> > > +	retval = wait_event_interruptible(stream->runtime->wait,
> > > +			stream->runtime->draining);
> > > +	if (retval)
> > > +		pr_err("Wait for drain failed %d\n", retval);
> > 
> > If you make it interruptible, it's no actual kernel error when user
> > interrupts the operation, so no reason to put the kernel error message
> > there.  At least, better to check whether it's a user interruption or
> > a system error.
> Agreed. I think I dont even need to make it interruptible. If user is
> terminating the stop will be invoked and then with above fix it should work. We
> can do away with interrutiple part :)
> 
> > > +
> > > +	wake_up(&stream->runtime->sleep);
> > > +	mutex_lock(&stream->device->lock);
> > > +
> > > +	return retval;
> > > +}
> > > +
> > >  static int snd_compr_drain(struct snd_compr_stream *stream)
> > >  {
> > >  	int retval;
> > > @@ -695,11 +720,17 @@ static int snd_compr_drain(struct snd_compr_stream *stream)
> > >  	if (stream->runtime->state == SNDRV_PCM_STATE_PREPARED ||
> > >  			stream->runtime->state == SNDRV_PCM_STATE_SETUP)
> > >  		return -EPERM;
> > > +
> > > +	stream->runtime->draining = 0;
> > >  	retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_DRAIN);
> > > -	if (!retval) {
> > > -		stream->runtime->state = SNDRV_PCM_STATE_DRAINING;
> > > +	if (retval) {
> > > +		pr_err("SND_COMPR_TRIGGER_DRAIN failed %d\n", retval);
> > >  		wake_up(&stream->runtime->sleep);
> > > +		return retval;
> > >  	}
> > > +
> > > +	retval = snd_compress_wait_for_drain(stream);
> > > +	stream->runtime->state = SNDRV_PCM_STATE_SETUP;
> > 
> > So, the runtime state goes to SETUP even if wait_for_drain() returns
> > an error?  In other words, compress core expects that the driver takes
> > care of the proper state change in the error handling?
> I guess it would make sense to communicate error. I havent added notion of
> status on drain complete. By adding another status flag in the callback we cna
> let driver tell us if error occured. Then pass the error in draining value and
> do the state change accordingly.

I think it's fine as in the current patch, I just wanted to make sure
that this runtime state change is the designed behavior.  If so, it'd
be better to document / comment clearly.


thanks,

Takashi

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

* [PATCH v3] ALSA: compress: fix drain calls blocking other compress functions
  2013-10-24  7:35 ` Vinod Koul
  2013-10-24  9:25   ` Takashi Iwai
@ 2013-10-24 11:07   ` Vinod Koul
  2013-10-30 14:47     ` Takashi Iwai
  2013-10-30 16:13     ` [PATCH v4] " Vinod Koul
  1 sibling, 2 replies; 19+ messages in thread
From: Vinod Koul @ 2013-10-24 11:07 UTC (permalink / raw)
  To: alsa-devel; +Cc: tiwai, Vinod Koul, broonie, lgirdwood

The drain and drain_notify callback were blocked by low level driver untill the
draining was complete. Due to this being invoked with big fat mutex held, others
ops like reading timestamp, calling pause, drop were blocked.

So to fix this we add a new snd_compr_drain_notify() API. This would be required
to be invoked by low level driver when drain or partial drain has been completed
by the DSP. Thus we make the drain and partial_drain callback as non blocking
and driver returns immediately after notifying DSP.
The waiting is done while relasing the lock so that other ops can go ahead.

Signed-off-by: Vinod Koul <vinod.koul@intel.com>
CC: stable@vger.kernel.org
---
v3:
 call snd_compr_drain_notify from compress_stop()
 rename draining -> drain_wake
 add some comments on state transistion after drain

v2:
 fix the 80 line warn
 move the state change to compress_drain()


 include/sound/compress_driver.h |   12 +++++++++++
 sound/core/compress_offload.c   |   41 ++++++++++++++++++++++++++++++++++++--
 2 files changed, 50 insertions(+), 3 deletions(-)

diff --git a/include/sound/compress_driver.h b/include/sound/compress_driver.h
index 9031a26..175ab32 100644
--- a/include/sound/compress_driver.h
+++ b/include/sound/compress_driver.h
@@ -48,6 +48,8 @@ struct snd_compr_ops;
  *	the ring buffer
  * @total_bytes_transferred: cumulative bytes transferred by offload DSP
  * @sleep: poll sleep
+ * @wait: drain wait queue
+ * @drain_wake: condition for drain wake
  */
 struct snd_compr_runtime {
 	snd_pcm_state_t state;
@@ -59,6 +61,8 @@ struct snd_compr_runtime {
 	u64 total_bytes_available;
 	u64 total_bytes_transferred;
 	wait_queue_head_t sleep;
+	wait_queue_head_t wait;
+	unsigned int drain_wake;
 	void *private_data;
 };
 
@@ -171,4 +175,12 @@ static inline void snd_compr_fragment_elapsed(struct snd_compr_stream *stream)
 	wake_up(&stream->runtime->sleep);
 }
 
+static inline void snd_compr_drain_notify(struct snd_compr_stream *stream)
+{
+	snd_BUG_ON(!stream);
+
+	stream->runtime->drain_wake = 1;
+	wake_up(&stream->runtime->wait);
+}
+
 #endif
diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c
index bea523a..3eb47d0 100644
--- a/sound/core/compress_offload.c
+++ b/sound/core/compress_offload.c
@@ -123,6 +123,7 @@ static int snd_compr_open(struct inode *inode, struct file *f)
 	}
 	runtime->state = SNDRV_PCM_STATE_OPEN;
 	init_waitqueue_head(&runtime->sleep);
+	init_waitqueue_head(&runtime->wait);
 	data->stream.runtime = runtime;
 	f->private_data = (void *)data;
 	mutex_lock(&compr->lock);
@@ -682,12 +683,34 @@ static int snd_compr_stop(struct snd_compr_stream *stream)
 	if (!retval) {
 		stream->runtime->state = SNDRV_PCM_STATE_SETUP;
 		wake_up(&stream->runtime->sleep);
+		snd_compr_drain_notify(stream);
 		stream->runtime->total_bytes_available = 0;
 		stream->runtime->total_bytes_transferred = 0;
 	}
 	return retval;
 }
 
+static int snd_compress_wait_for_drain(struct snd_compr_stream *stream)
+{
+	/*
+	 * We are called with lock held. So drop the lock while we wait for
+	 * drain complete notfication from the driver
+	 *
+	 * It is expected that driver will notify the drain completion and then
+	 * stream will be moved to SETUP state, even if draining resulted in an
+	 * error. We can trigger next track after this.
+	 */
+	stream->runtime->state = SNDRV_PCM_STATE_DRAINING;
+	mutex_unlock(&stream->device->lock);
+
+	wait_event(stream->runtime->wait, stream->runtime->drain_wake);
+
+	wake_up(&stream->runtime->sleep);
+	mutex_lock(&stream->device->lock);
+
+	return 0;
+}
+
 static int snd_compr_drain(struct snd_compr_stream *stream)
 {
 	int retval;
@@ -695,11 +718,17 @@ static int snd_compr_drain(struct snd_compr_stream *stream)
 	if (stream->runtime->state == SNDRV_PCM_STATE_PREPARED ||
 			stream->runtime->state == SNDRV_PCM_STATE_SETUP)
 		return -EPERM;
+
+	stream->runtime->drain_wake = 0;
 	retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_DRAIN);
-	if (!retval) {
-		stream->runtime->state = SNDRV_PCM_STATE_DRAINING;
+	if (retval) {
+		pr_err("SND_COMPR_TRIGGER_DRAIN failed %d\n", retval);
 		wake_up(&stream->runtime->sleep);
+		return retval;
 	}
+
+	retval = snd_compress_wait_for_drain(stream);
+	stream->runtime->state = SNDRV_PCM_STATE_SETUP;
 	return retval;
 }
 
@@ -735,10 +764,16 @@ static int snd_compr_partial_drain(struct snd_compr_stream *stream)
 	if (stream->next_track == false)
 		return -EPERM;
 
+	stream->runtime->drain_wake = 0;
 	retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_PARTIAL_DRAIN);
+	if (retval) {
+		pr_err("Partial drain returned failure\n");
+		wake_up(&stream->runtime->sleep);
+		return retval;
+	}
 
 	stream->next_track = false;
-	return retval;
+	return snd_compress_wait_for_drain(stream);
 }
 
 static long snd_compr_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
-- 
1.7.0.4

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

* Re: [PATCH] ALSA: compress: fix drain calls blocking other compress functions
  2013-10-24 12:00           ` Takashi Iwai
@ 2013-10-24 11:08             ` Vinod Koul
  0 siblings, 0 replies; 19+ messages in thread
From: Vinod Koul @ 2013-10-24 11:08 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: alsa-devel, broonie, lgirdwood

On Thu, Oct 24, 2013 at 02:00:39PM +0200, Takashi Iwai wrote:
> > hmmm, yes it needs a better name :)
> > perhaps drain_condition or drain_wake would be apt!
> 
> Yes, drain_wake would be more self-explanatory.
Ok sent v3 with this a few moment ago :)

--
~Vinod

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

* Re: [PATCH] ALSA: compress: fix drain calls blocking other compress functions
  2013-10-24 10:35         ` Vinod Koul
@ 2013-10-24 12:00           ` Takashi Iwai
  2013-10-24 11:08             ` Vinod Koul
  0 siblings, 1 reply; 19+ messages in thread
From: Takashi Iwai @ 2013-10-24 12:00 UTC (permalink / raw)
  To: Vinod Koul; +Cc: alsa-devel, broonie, lgirdwood

At Thu, 24 Oct 2013 16:05:05 +0530,
Vinod Koul wrote:
> 
> On Thu, Oct 24, 2013 at 12:59:18PM +0200, Takashi Iwai wrote:
> > At Thu, 24 Oct 2013 14:57:24 +0530,
> > Vinod Koul wrote:
> > > 
> > > On Thu, Oct 24, 2013 at 11:25:35AM +0200, Takashi Iwai wrote:
> > > > At Thu, 24 Oct 2013 13:05:41 +0530,
> > > > Vinod Koul wrote:
> > > > > 
> > > > > The drain and drain_notify callback were blocked by low level driver untill the
> > > > > draining was complete. Due to this being invoked with big fat mutex held, others
> > > > > ops like reading timestamp, calling pause, drop were blocked.
> > > > > 
> > > > > So to fix this we add a new snd_compr_drain_notify() API. This would be required
> > > > > to be invoked by low level driver when drain or partial drain has been completed
> > > > > by the DSP. Thus we make the drain and partial_drain callback as non blocking
> > > > > and driver returns immediately after notifying DSP.
> > > > > The waiting is done while relasing the lock so that other ops can go ahead.
> > > > > 
> > > > > Signed-off-by: Vinod Koul <vinod.koul@intel.com>
> > > > > CC: stable@vger.kernel.org
> > > > > ---
> > > > > v2:
> > > > >  fix the 80 line warn
> > > > >  move the state change to compress_drain()
> > > > > 
> > > > >  include/sound/compress_driver.h |   12 ++++++++++
> > > > >  sound/core/compress_offload.c   |   43 ++++++++++++++++++++++++++++++++++++--
> > > > >  2 files changed, 52 insertions(+), 3 deletions(-)
> > > > > 
> > > > > diff --git a/include/sound/compress_driver.h b/include/sound/compress_driver.h
> > > > > index 9031a26..eff5f84 100644
> > > > > --- a/include/sound/compress_driver.h
> > > > > +++ b/include/sound/compress_driver.h
> > > > > @@ -48,6 +48,8 @@ struct snd_compr_ops;
> > > > >   *	the ring buffer
> > > > >   * @total_bytes_transferred: cumulative bytes transferred by offload DSP
> > > > >   * @sleep: poll sleep
> > > > > + * @wait: drain wait queuq
> > > > > + * @draining: draining wait condition
> > > > 
> > > > What does this mean exactly?  I thought draining = 1 meaning that the
> > > > stream is draining (i.e. waiting for the finish notification), but
> > > > it looks opposite in your code below...
> > > What i meant was the draining wait conditions which would be used for waiting.
> > > Before wait the caller will set to 0 and on completion the callback will set to
> > > 1 and wake up.
> > 
> > Well, it's not so intuitive as the flag being named "draining".
> > Please give a more detailed comment (or name it a bit less-confusing
> > one :)
> hmmm, yes it needs a better name :)
> perhaps drain_condition or drain_wake would be apt!

Yes, drain_wake would be more self-explanatory.

> > > > > +
> > > > > +	wake_up(&stream->runtime->sleep);
> > > > > +	mutex_lock(&stream->device->lock);
> > > > > +
> > > > > +	return retval;
> > > > > +}
> > > > > +
> > > > >  static int snd_compr_drain(struct snd_compr_stream *stream)
> > > > >  {
> > > > >  	int retval;
> > > > > @@ -695,11 +720,17 @@ static int snd_compr_drain(struct snd_compr_stream *stream)
> > > > >  	if (stream->runtime->state == SNDRV_PCM_STATE_PREPARED ||
> > > > >  			stream->runtime->state == SNDRV_PCM_STATE_SETUP)
> > > > >  		return -EPERM;
> > > > > +
> > > > > +	stream->runtime->draining = 0;
> > > > >  	retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_DRAIN);
> > > > > -	if (!retval) {
> > > > > -		stream->runtime->state = SNDRV_PCM_STATE_DRAINING;
> > > > > +	if (retval) {
> > > > > +		pr_err("SND_COMPR_TRIGGER_DRAIN failed %d\n", retval);
> > > > >  		wake_up(&stream->runtime->sleep);
> > > > > +		return retval;
> > > > >  	}
> > > > > +
> > > > > +	retval = snd_compress_wait_for_drain(stream);
> > > > > +	stream->runtime->state = SNDRV_PCM_STATE_SETUP;
> > > > 
> > > > So, the runtime state goes to SETUP even if wait_for_drain() returns
> > > > an error?  In other words, compress core expects that the driver takes
> > > > care of the proper state change in the error handling?
> > > I guess it would make sense to communicate error. I havent added notion of
> > > status on drain complete. By adding another status flag in the callback we cna
> > > let driver tell us if error occured. Then pass the error in draining value and
> > > do the state change accordingly.
> > 
> > I think it's fine as in the current patch, I just wanted to make sure
> > that this runtime state change is the designed behavior.  If so, it'd
> > be better to document / comment clearly.
> Sure, lets add that explictly!

OK, thanks.


Takashi

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

* Re: [PATCH v3] ALSA: compress: fix drain calls blocking other compress functions
  2013-10-24 11:07   ` [PATCH v3] " Vinod Koul
@ 2013-10-30 14:47     ` Takashi Iwai
  2013-10-30 14:58       ` Vinod Koul
  2013-10-30 16:13     ` [PATCH v4] " Vinod Koul
  1 sibling, 1 reply; 19+ messages in thread
From: Takashi Iwai @ 2013-10-30 14:47 UTC (permalink / raw)
  To: Vinod Koul; +Cc: alsa-devel, broonie, lgirdwood

At Thu, 24 Oct 2013 16:37:31 +0530,
Vinod Koul wrote:
> 
> The drain and drain_notify callback were blocked by low level driver untill the
> draining was complete. Due to this being invoked with big fat mutex held, others
> ops like reading timestamp, calling pause, drop were blocked.
> 
> So to fix this we add a new snd_compr_drain_notify() API. This would be required
> to be invoked by low level driver when drain or partial drain has been completed
> by the DSP. Thus we make the drain and partial_drain callback as non blocking
> and driver returns immediately after notifying DSP.
> The waiting is done while relasing the lock so that other ops can go ahead.
> 
> Signed-off-by: Vinod Koul <vinod.koul@intel.com>
> CC: stable@vger.kernel.org
> ---
> v3:
>  call snd_compr_drain_notify from compress_stop()
>  rename draining -> drain_wake
>  add some comments on state transistion after drain
> 
> v2:
>  fix the 80 line warn
>  move the state change to compress_drain()
> 
> 
>  include/sound/compress_driver.h |   12 +++++++++++
>  sound/core/compress_offload.c   |   41 ++++++++++++++++++++++++++++++++++++--
>  2 files changed, 50 insertions(+), 3 deletions(-)
> 
> diff --git a/include/sound/compress_driver.h b/include/sound/compress_driver.h
> index 9031a26..175ab32 100644
> --- a/include/sound/compress_driver.h
> +++ b/include/sound/compress_driver.h
> @@ -48,6 +48,8 @@ struct snd_compr_ops;
>   *	the ring buffer
>   * @total_bytes_transferred: cumulative bytes transferred by offload DSP
>   * @sleep: poll sleep
> + * @wait: drain wait queue
> + * @drain_wake: condition for drain wake
>   */
>  struct snd_compr_runtime {
>  	snd_pcm_state_t state;
> @@ -59,6 +61,8 @@ struct snd_compr_runtime {
>  	u64 total_bytes_available;
>  	u64 total_bytes_transferred;
>  	wait_queue_head_t sleep;
> +	wait_queue_head_t wait;
> +	unsigned int drain_wake;
>  	void *private_data;
>  };
>  
> @@ -171,4 +175,12 @@ static inline void snd_compr_fragment_elapsed(struct snd_compr_stream *stream)
>  	wake_up(&stream->runtime->sleep);
>  }
>  
> +static inline void snd_compr_drain_notify(struct snd_compr_stream *stream)
> +{
> +	snd_BUG_ON(!stream);

Should return, otherwise you'll get Oops:

	if (snd_BUG_ON(!stream))
		return;

> +
> +	stream->runtime->drain_wake = 1;
> +	wake_up(&stream->runtime->wait);
> +}
> +
>  #endif
> diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c
> index bea523a..3eb47d0 100644
> --- a/sound/core/compress_offload.c
> +++ b/sound/core/compress_offload.c
> @@ -123,6 +123,7 @@ static int snd_compr_open(struct inode *inode, struct file *f)
>  	}
>  	runtime->state = SNDRV_PCM_STATE_OPEN;
>  	init_waitqueue_head(&runtime->sleep);
> +	init_waitqueue_head(&runtime->wait);
>  	data->stream.runtime = runtime;
>  	f->private_data = (void *)data;
>  	mutex_lock(&compr->lock);
> @@ -682,12 +683,34 @@ static int snd_compr_stop(struct snd_compr_stream *stream)
>  	if (!retval) {
>  		stream->runtime->state = SNDRV_PCM_STATE_SETUP;
>  		wake_up(&stream->runtime->sleep);
> +		snd_compr_drain_notify(stream);
>  		stream->runtime->total_bytes_available = 0;
>  		stream->runtime->total_bytes_transferred = 0;
>  	}
>  	return retval;
>  }
>  
> +static int snd_compress_wait_for_drain(struct snd_compr_stream *stream)
> +{
> +	/*
> +	 * We are called with lock held. So drop the lock while we wait for
> +	 * drain complete notfication from the driver
> +	 *
> +	 * It is expected that driver will notify the drain completion and then
> +	 * stream will be moved to SETUP state, even if draining resulted in an
> +	 * error. We can trigger next track after this.
> +	 */
> +	stream->runtime->state = SNDRV_PCM_STATE_DRAINING;
> +	mutex_unlock(&stream->device->lock);
> +
> +	wait_event(stream->runtime->wait, stream->runtime->drain_wake);

Don't you really want the interruption?
Or use wait_event_interruptible() and return error appropriately.

> +
> +	wake_up(&stream->runtime->sleep);
> +	mutex_lock(&stream->device->lock);
> +
> +	return 0;
> +}
> +
>  static int snd_compr_drain(struct snd_compr_stream *stream)
>  {
>  	int retval;
> @@ -695,11 +718,17 @@ static int snd_compr_drain(struct snd_compr_stream *stream)
>  	if (stream->runtime->state == SNDRV_PCM_STATE_PREPARED ||
>  			stream->runtime->state == SNDRV_PCM_STATE_SETUP)
>  		return -EPERM;
> +
> +	stream->runtime->drain_wake = 0;
>  	retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_DRAIN);
> -	if (!retval) {
> -		stream->runtime->state = SNDRV_PCM_STATE_DRAINING;
> +	if (retval) {
> +		pr_err("SND_COMPR_TRIGGER_DRAIN failed %d\n", retval);

I don't think spamming such an error is good.  The trigger error can
happen often repeatedly once when it happens.  Such a message is at
most a debug message, so use pr_debug(), if you still want to keep it
in the core code.

>  		wake_up(&stream->runtime->sleep);
> +		return retval;
>  	}
> +
> +	retval = snd_compress_wait_for_drain(stream);
> +	stream->runtime->state = SNDRV_PCM_STATE_SETUP;
>  	return retval;
>  }
>  
> @@ -735,10 +764,16 @@ static int snd_compr_partial_drain(struct snd_compr_stream *stream)
>  	if (stream->next_track == false)
>  		return -EPERM;
>  
> +	stream->runtime->drain_wake = 0;
>  	retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_PARTIAL_DRAIN);
> +	if (retval) {
> +		pr_err("Partial drain returned failure\n");

Ditto.

> +		wake_up(&stream->runtime->sleep);
> +		return retval;
> +	}
>  
>  	stream->next_track = false;
> -	return retval;
> +	return snd_compress_wait_for_drain(stream);
>  }
>  
>  static long snd_compr_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
> -- 
> 1.7.0.4
> 


Takashi

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

* Re: [PATCH v3] ALSA: compress: fix drain calls blocking other compress functions
  2013-10-30 14:47     ` Takashi Iwai
@ 2013-10-30 14:58       ` Vinod Koul
  2013-10-30 16:01         ` Takashi Iwai
  0 siblings, 1 reply; 19+ messages in thread
From: Vinod Koul @ 2013-10-30 14:58 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: alsa-devel, broonie, lgirdwood

On Wed, Oct 30, 2013 at 03:47:45PM +0100, Takashi Iwai wrote:
> At Thu, 24 Oct 2013 16:37:31 +0530,
> Vinod Koul wrote:
> > 
> > The drain and drain_notify callback were blocked by low level driver untill the
> > draining was complete. Due to this being invoked with big fat mutex held, others
> > ops like reading timestamp, calling pause, drop were blocked.
> > 
> > So to fix this we add a new snd_compr_drain_notify() API. This would be required
> > to be invoked by low level driver when drain or partial drain has been completed
> > by the DSP. Thus we make the drain and partial_drain callback as non blocking
> > and driver returns immediately after notifying DSP.
> > The waiting is done while relasing the lock so that other ops can go ahead.
> > 
> > Signed-off-by: Vinod Koul <vinod.koul@intel.com>
> > CC: stable@vger.kernel.org
> > ---
> > v3:
> >  call snd_compr_drain_notify from compress_stop()
> >  rename draining -> drain_wake
> >  add some comments on state transistion after drain
> > 
> > v2:
> >  fix the 80 line warn
> >  move the state change to compress_drain()
> > 
> > 
> >  include/sound/compress_driver.h |   12 +++++++++++
> >  sound/core/compress_offload.c   |   41 ++++++++++++++++++++++++++++++++++++--
> >  2 files changed, 50 insertions(+), 3 deletions(-)
> > 
> > diff --git a/include/sound/compress_driver.h b/include/sound/compress_driver.h
> > index 9031a26..175ab32 100644
> > --- a/include/sound/compress_driver.h
> > +++ b/include/sound/compress_driver.h
> > @@ -48,6 +48,8 @@ struct snd_compr_ops;
> >   *	the ring buffer
> >   * @total_bytes_transferred: cumulative bytes transferred by offload DSP
> >   * @sleep: poll sleep
> > + * @wait: drain wait queue
> > + * @drain_wake: condition for drain wake
> >   */
> >  struct snd_compr_runtime {
> >  	snd_pcm_state_t state;
> > @@ -59,6 +61,8 @@ struct snd_compr_runtime {
> >  	u64 total_bytes_available;
> >  	u64 total_bytes_transferred;
> >  	wait_queue_head_t sleep;
> > +	wait_queue_head_t wait;
> > +	unsigned int drain_wake;
> >  	void *private_data;
> >  };
> >  
> > @@ -171,4 +175,12 @@ static inline void snd_compr_fragment_elapsed(struct snd_compr_stream *stream)
> >  	wake_up(&stream->runtime->sleep);
> >  }
> >  
> > +static inline void snd_compr_drain_notify(struct snd_compr_stream *stream)
> > +{
> > +	snd_BUG_ON(!stream);
> 
> Should return, otherwise you'll get Oops:
> 
> 	if (snd_BUG_ON(!stream))
> 		return;
ah, missed that!

> > +
> > +	stream->runtime->drain_wake = 1;
> > +	wake_up(&stream->runtime->wait);
> > +}
> > +
> >  #endif
> > diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c
> > index bea523a..3eb47d0 100644
> > --- a/sound/core/compress_offload.c
> > +++ b/sound/core/compress_offload.c
> > @@ -123,6 +123,7 @@ static int snd_compr_open(struct inode *inode, struct file *f)
> >  	}
> >  	runtime->state = SNDRV_PCM_STATE_OPEN;
> >  	init_waitqueue_head(&runtime->sleep);
> > +	init_waitqueue_head(&runtime->wait);
> >  	data->stream.runtime = runtime;
> >  	f->private_data = (void *)data;
> >  	mutex_lock(&compr->lock);
> > @@ -682,12 +683,34 @@ static int snd_compr_stop(struct snd_compr_stream *stream)
> >  	if (!retval) {
> >  		stream->runtime->state = SNDRV_PCM_STATE_SETUP;
> >  		wake_up(&stream->runtime->sleep);
> > +		snd_compr_drain_notify(stream);
> >  		stream->runtime->total_bytes_available = 0;
> >  		stream->runtime->total_bytes_transferred = 0;
> >  	}
> >  	return retval;
> >  }
> >  
> > +static int snd_compress_wait_for_drain(struct snd_compr_stream *stream)
> > +{
> > +	/*
> > +	 * We are called with lock held. So drop the lock while we wait for
> > +	 * drain complete notfication from the driver
> > +	 *
> > +	 * It is expected that driver will notify the drain completion and then
> > +	 * stream will be moved to SETUP state, even if draining resulted in an
> > +	 * error. We can trigger next track after this.
> > +	 */
> > +	stream->runtime->state = SNDRV_PCM_STATE_DRAINING;
> > +	mutex_unlock(&stream->device->lock);
> > +
> > +	wait_event(stream->runtime->wait, stream->runtime->drain_wake);
> 
> Don't you really want the interruption?
> Or use wait_event_interruptible() and return error appropriately.
any interruption from usermode should trigger the compress_stop/compress_free
which will unblock this. I dont see the need to have interruptible here

> > +
> > +	wake_up(&stream->runtime->sleep);
> > +	mutex_lock(&stream->device->lock);
> > +
> > +	return 0;
> > +}
> > +
> >  static int snd_compr_drain(struct snd_compr_stream *stream)
> >  {
> >  	int retval;
> > @@ -695,11 +718,17 @@ static int snd_compr_drain(struct snd_compr_stream *stream)
> >  	if (stream->runtime->state == SNDRV_PCM_STATE_PREPARED ||
> >  			stream->runtime->state == SNDRV_PCM_STATE_SETUP)
> >  		return -EPERM;
> > +
> > +	stream->runtime->drain_wake = 0;
> >  	retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_DRAIN);
> > -	if (!retval) {
> > -		stream->runtime->state = SNDRV_PCM_STATE_DRAINING;
> > +	if (retval) {
> > +		pr_err("SND_COMPR_TRIGGER_DRAIN failed %d\n", retval);
> 
> I don't think spamming such an error is good.  The trigger error can
> happen often repeatedly once when it happens.  Such a message is at
> most a debug message, so use pr_debug(), if you still want to keep it
> in the core code.
Sure...

Thanks for the quick review, will send v4 before I hit the bed today!

--
~Vinod
> >  		wake_up(&stream->runtime->sleep);
> > +		return retval;
> >  	}
> > +
> > +	retval = snd_compress_wait_for_drain(stream);
> > +	stream->runtime->state = SNDRV_PCM_STATE_SETUP;
> >  	return retval;
> >  }
> >  
> > @@ -735,10 +764,16 @@ static int snd_compr_partial_drain(struct snd_compr_stream *stream)
> >  	if (stream->next_track == false)
> >  		return -EPERM;
> >  
> > +	stream->runtime->drain_wake = 0;
> >  	retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_PARTIAL_DRAIN);
> > +	if (retval) {
> > +		pr_err("Partial drain returned failure\n");
> 
> Ditto.
> 
> > +		wake_up(&stream->runtime->sleep);
> > +		return retval;
> > +	}
> >  
> >  	stream->next_track = false;
> > -	return retval;
> > +	return snd_compress_wait_for_drain(stream);
> >  }
> >  
> >  static long snd_compr_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
> > -- 
> > 1.7.0.4
> > 
> 
> 
> Takashi

-- 

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

* Re: [PATCH v3] ALSA: compress: fix drain calls blocking other compress functions
  2013-10-30 16:01         ` Takashi Iwai
@ 2013-10-30 15:18           ` Vinod Koul
  2013-10-30 16:24             ` Takashi Iwai
  0 siblings, 1 reply; 19+ messages in thread
From: Vinod Koul @ 2013-10-30 15:18 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: alsa-devel, broonie, lgirdwood

On Wed, Oct 30, 2013 at 05:01:38PM +0100, Takashi Iwai wrote:
 
> > > > +static int snd_compress_wait_for_drain(struct snd_compr_stream *stream)
> > > > +{
> > > > +	/*
> > > > +	 * We are called with lock held. So drop the lock while we wait for
> > > > +	 * drain complete notfication from the driver
> > > > +	 *
> > > > +	 * It is expected that driver will notify the drain completion and then
> > > > +	 * stream will be moved to SETUP state, even if draining resulted in an
> > > > +	 * error. We can trigger next track after this.
> > > > +	 */
> > > > +	stream->runtime->state = SNDRV_PCM_STATE_DRAINING;
> > > > +	mutex_unlock(&stream->device->lock);
> > > > +
> > > > +	wait_event(stream->runtime->wait, stream->runtime->drain_wake);
> > > 
> > > Don't you really want the interruption?
> > > Or use wait_event_interruptible() and return error appropriately.
> > any interruption from usermode should trigger the compress_stop/compress_free
> 
> ... but how?
> 
> > which will unblock this. I dont see the need to have interruptible here
> 
> Suppose you're running a single thread program.  Then who triggers the
> stop?
Your signal catcher which should be registered for signals intrested and then
calls stop or free. Assuming this should be considered single threaded. IIRC,
this is how you do in aplay, right?

Btw am okay to make it interruptible, but since can be done in usermode and
closed which unblocks this I would prefer this way. Pls do let me know :)
--
~Vinod

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

* Re: [PATCH v3] ALSA: compress: fix drain calls blocking other compress functions
  2013-10-30 16:24             ` Takashi Iwai
@ 2013-10-30 15:33               ` Vinod Koul
  2013-10-30 16:38                 ` Takashi Iwai
  0 siblings, 1 reply; 19+ messages in thread
From: Vinod Koul @ 2013-10-30 15:33 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: alsa-devel, broonie, lgirdwood

On Wed, Oct 30, 2013 at 05:24:12PM +0100, Takashi Iwai wrote:
> At Wed, 30 Oct 2013 20:48:40 +0530,
> Vinod Koul wrote:
> > 
> > On Wed, Oct 30, 2013 at 05:01:38PM +0100, Takashi Iwai wrote:
> >  
> > > > > > +static int snd_compress_wait_for_drain(struct snd_compr_stream *stream)
> > > > > > +{
> > > > > > +	/*
> > > > > > +	 * We are called with lock held. So drop the lock while we wait for
> > > > > > +	 * drain complete notfication from the driver
> > > > > > +	 *
> > > > > > +	 * It is expected that driver will notify the drain completion and then
> > > > > > +	 * stream will be moved to SETUP state, even if draining resulted in an
> > > > > > +	 * error. We can trigger next track after this.
> > > > > > +	 */
> > > > > > +	stream->runtime->state = SNDRV_PCM_STATE_DRAINING;
> > > > > > +	mutex_unlock(&stream->device->lock);
> > > > > > +
> > > > > > +	wait_event(stream->runtime->wait, stream->runtime->drain_wake);
> > > > > 
> > > > > Don't you really want the interruption?
> > > > > Or use wait_event_interruptible() and return error appropriately.
> > > > any interruption from usermode should trigger the compress_stop/compress_free
> > > 
> > > ... but how?
> > > 
> > > > which will unblock this. I dont see the need to have interruptible here
> > > 
> > > Suppose you're running a single thread program.  Then who triggers the
> > > stop?
> > Your signal catcher which should be registered for signals intrested and then
> > calls stop or free. Assuming this should be considered single threaded. IIRC,
> > this is how you do in aplay, right?
> 
> No, issuing an ioctl in a signal isn't guaranteed to work at all.
Okay thanks for pointing out, do you know why we have that limitation. Btw
closing the fd should have same effect as free would trigger the stop internally

> You shouldn't code the kernel driver relying on that userspace
> behavior.
Ok, will update it

--
~Vinod

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

* Re: [PATCH v3] ALSA: compress: fix drain calls blocking other compress functions
  2013-10-30 14:58       ` Vinod Koul
@ 2013-10-30 16:01         ` Takashi Iwai
  2013-10-30 15:18           ` Vinod Koul
  0 siblings, 1 reply; 19+ messages in thread
From: Takashi Iwai @ 2013-10-30 16:01 UTC (permalink / raw)
  To: Vinod Koul; +Cc: alsa-devel, broonie, lgirdwood

At Wed, 30 Oct 2013 20:28:11 +0530,
Vinod Koul wrote:
> 
> On Wed, Oct 30, 2013 at 03:47:45PM +0100, Takashi Iwai wrote:
> > At Thu, 24 Oct 2013 16:37:31 +0530,
> > Vinod Koul wrote:
> > > 
> > > The drain and drain_notify callback were blocked by low level driver untill the
> > > draining was complete. Due to this being invoked with big fat mutex held, others
> > > ops like reading timestamp, calling pause, drop were blocked.
> > > 
> > > So to fix this we add a new snd_compr_drain_notify() API. This would be required
> > > to be invoked by low level driver when drain or partial drain has been completed
> > > by the DSP. Thus we make the drain and partial_drain callback as non blocking
> > > and driver returns immediately after notifying DSP.
> > > The waiting is done while relasing the lock so that other ops can go ahead.
> > > 
> > > Signed-off-by: Vinod Koul <vinod.koul@intel.com>
> > > CC: stable@vger.kernel.org
> > > ---
> > > v3:
> > >  call snd_compr_drain_notify from compress_stop()
> > >  rename draining -> drain_wake
> > >  add some comments on state transistion after drain
> > > 
> > > v2:
> > >  fix the 80 line warn
> > >  move the state change to compress_drain()
> > > 
> > > 
> > >  include/sound/compress_driver.h |   12 +++++++++++
> > >  sound/core/compress_offload.c   |   41 ++++++++++++++++++++++++++++++++++++--
> > >  2 files changed, 50 insertions(+), 3 deletions(-)
> > > 
> > > diff --git a/include/sound/compress_driver.h b/include/sound/compress_driver.h
> > > index 9031a26..175ab32 100644
> > > --- a/include/sound/compress_driver.h
> > > +++ b/include/sound/compress_driver.h
> > > @@ -48,6 +48,8 @@ struct snd_compr_ops;
> > >   *	the ring buffer
> > >   * @total_bytes_transferred: cumulative bytes transferred by offload DSP
> > >   * @sleep: poll sleep
> > > + * @wait: drain wait queue
> > > + * @drain_wake: condition for drain wake
> > >   */
> > >  struct snd_compr_runtime {
> > >  	snd_pcm_state_t state;
> > > @@ -59,6 +61,8 @@ struct snd_compr_runtime {
> > >  	u64 total_bytes_available;
> > >  	u64 total_bytes_transferred;
> > >  	wait_queue_head_t sleep;
> > > +	wait_queue_head_t wait;
> > > +	unsigned int drain_wake;
> > >  	void *private_data;
> > >  };
> > >  
> > > @@ -171,4 +175,12 @@ static inline void snd_compr_fragment_elapsed(struct snd_compr_stream *stream)
> > >  	wake_up(&stream->runtime->sleep);
> > >  }
> > >  
> > > +static inline void snd_compr_drain_notify(struct snd_compr_stream *stream)
> > > +{
> > > +	snd_BUG_ON(!stream);
> > 
> > Should return, otherwise you'll get Oops:
> > 
> > 	if (snd_BUG_ON(!stream))
> > 		return;
> ah, missed that!
> 
> > > +
> > > +	stream->runtime->drain_wake = 1;
> > > +	wake_up(&stream->runtime->wait);
> > > +}
> > > +
> > >  #endif
> > > diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c
> > > index bea523a..3eb47d0 100644
> > > --- a/sound/core/compress_offload.c
> > > +++ b/sound/core/compress_offload.c
> > > @@ -123,6 +123,7 @@ static int snd_compr_open(struct inode *inode, struct file *f)
> > >  	}
> > >  	runtime->state = SNDRV_PCM_STATE_OPEN;
> > >  	init_waitqueue_head(&runtime->sleep);
> > > +	init_waitqueue_head(&runtime->wait);
> > >  	data->stream.runtime = runtime;
> > >  	f->private_data = (void *)data;
> > >  	mutex_lock(&compr->lock);
> > > @@ -682,12 +683,34 @@ static int snd_compr_stop(struct snd_compr_stream *stream)
> > >  	if (!retval) {
> > >  		stream->runtime->state = SNDRV_PCM_STATE_SETUP;
> > >  		wake_up(&stream->runtime->sleep);
> > > +		snd_compr_drain_notify(stream);
> > >  		stream->runtime->total_bytes_available = 0;
> > >  		stream->runtime->total_bytes_transferred = 0;
> > >  	}
> > >  	return retval;
> > >  }
> > >  
> > > +static int snd_compress_wait_for_drain(struct snd_compr_stream *stream)
> > > +{
> > > +	/*
> > > +	 * We are called with lock held. So drop the lock while we wait for
> > > +	 * drain complete notfication from the driver
> > > +	 *
> > > +	 * It is expected that driver will notify the drain completion and then
> > > +	 * stream will be moved to SETUP state, even if draining resulted in an
> > > +	 * error. We can trigger next track after this.
> > > +	 */
> > > +	stream->runtime->state = SNDRV_PCM_STATE_DRAINING;
> > > +	mutex_unlock(&stream->device->lock);
> > > +
> > > +	wait_event(stream->runtime->wait, stream->runtime->drain_wake);
> > 
> > Don't you really want the interruption?
> > Or use wait_event_interruptible() and return error appropriately.
> any interruption from usermode should trigger the compress_stop/compress_free

... but how?

> which will unblock this. I dont see the need to have interruptible here

Suppose you're running a single thread program.  Then who triggers the
stop?


Takashi

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

* [PATCH v4] ALSA: compress: fix drain calls blocking other compress functions
  2013-10-24 11:07   ` [PATCH v3] " Vinod Koul
  2013-10-30 14:47     ` Takashi Iwai
@ 2013-10-30 16:13     ` Vinod Koul
  2013-10-31  7:40       ` Takashi Iwai
  1 sibling, 1 reply; 19+ messages in thread
From: Vinod Koul @ 2013-10-30 16:13 UTC (permalink / raw)
  To: alsa-devel; +Cc: tiwai, Vinod Koul, broonie, lgirdwood

The drain and drain_notify callback were blocked by low level driver untill the
draining was complete. Due to this being invoked with big fat mutex held, others
ops like reading timestamp, calling pause, drop were blocked.

So to fix this we add a new snd_compr_drain_notify() API. This would be required
to be invoked by low level driver when drain or partial drain has been completed
by the DSP. Thus we make the drain and partial_drain callback as non blocking
and driver returns immediately after notifying DSP.
The waiting is done while relasing the lock so that other ops can go ahead.

Signed-off-by: Vinod Koul <vinod.koul@intel.com>
CC: stable@vger.kernel.org
---
v4:
 move pr_err -> pr_debug to avoid spamming kernel log
 make wait in drain interruptible
v3:
 call snd_compr_drain_notify from compress_stop()
 rename draining -> drain_wake
 add some comments on state transistion after drain
v2:
 fix the 80 line warn
 move the state change to compress_drain()


 include/sound/compress_driver.h |   13 +++++++++
 sound/core/compress_offload.c   |   55 ++++++++++++++++++++++++++++++++++++--
 2 files changed, 65 insertions(+), 3 deletions(-)

diff --git a/include/sound/compress_driver.h b/include/sound/compress_driver.h
index 9031a26..e723935 100644
--- a/include/sound/compress_driver.h
+++ b/include/sound/compress_driver.h
@@ -48,6 +48,8 @@ struct snd_compr_ops;
  *	the ring buffer
  * @total_bytes_transferred: cumulative bytes transferred by offload DSP
  * @sleep: poll sleep
+ * @wait: drain wait queue
+ * @drain_wake: condition for drain wake
  */
 struct snd_compr_runtime {
 	snd_pcm_state_t state;
@@ -59,6 +61,8 @@ struct snd_compr_runtime {
 	u64 total_bytes_available;
 	u64 total_bytes_transferred;
 	wait_queue_head_t sleep;
+	wait_queue_head_t wait;
+	unsigned int drain_wake;
 	void *private_data;
 };
 
@@ -171,4 +175,13 @@ static inline void snd_compr_fragment_elapsed(struct snd_compr_stream *stream)
 	wake_up(&stream->runtime->sleep);
 }
 
+static inline void snd_compr_drain_notify(struct snd_compr_stream *stream)
+{
+	if (snd_BUG_ON(!stream))
+		return;
+
+	stream->runtime->drain_wake = 1;
+	wake_up(&stream->runtime->wait);
+}
+
 #endif
diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c
index bea523a..645cec5 100644
--- a/sound/core/compress_offload.c
+++ b/sound/core/compress_offload.c
@@ -123,6 +123,7 @@ static int snd_compr_open(struct inode *inode, struct file *f)
 	}
 	runtime->state = SNDRV_PCM_STATE_OPEN;
 	init_waitqueue_head(&runtime->sleep);
+	init_waitqueue_head(&runtime->wait);
 	data->stream.runtime = runtime;
 	f->private_data = (void *)data;
 	mutex_lock(&compr->lock);
@@ -682,12 +683,48 @@ static int snd_compr_stop(struct snd_compr_stream *stream)
 	if (!retval) {
 		stream->runtime->state = SNDRV_PCM_STATE_SETUP;
 		wake_up(&stream->runtime->sleep);
+		snd_compr_drain_notify(stream);
 		stream->runtime->total_bytes_available = 0;
 		stream->runtime->total_bytes_transferred = 0;
 	}
 	return retval;
 }
 
+static int snd_compress_wait_for_drain(struct snd_compr_stream *stream)
+{
+	int ret;
+
+	/*
+	 * We are called with lock held. So drop the lock while we wait for
+	 * drain complete notfication from the driver
+	 *
+	 * It is expected that driver will notify the drain completion and then
+	 * stream will be moved to SETUP state, even if draining resulted in an
+	 * error. We can trigger next track after this.
+	 */
+	stream->runtime->state = SNDRV_PCM_STATE_DRAINING;
+	mutex_unlock(&stream->device->lock);
+
+	/* we wait for drain to complete here, drain can return when
+	 * interruption occurred, wait returned error or success.
+	 * For the first two cases we don't do anything different here and
+	 * return after waking up
+	 */
+
+	ret = wait_event_interruptible(stream->runtime->wait,
+			stream->runtime->drain_wake);
+	if (ret == -ERESTARTSYS)
+		pr_debug("wait aborted by a signal");
+	else if (ret)
+		pr_debug("wait for drain failed with %d\n", ret);
+
+
+	wake_up(&stream->runtime->sleep);
+	mutex_lock(&stream->device->lock);
+
+	return ret;
+}
+
 static int snd_compr_drain(struct snd_compr_stream *stream)
 {
 	int retval;
@@ -695,11 +732,17 @@ static int snd_compr_drain(struct snd_compr_stream *stream)
 	if (stream->runtime->state == SNDRV_PCM_STATE_PREPARED ||
 			stream->runtime->state == SNDRV_PCM_STATE_SETUP)
 		return -EPERM;
+
+	stream->runtime->drain_wake = 0;
 	retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_DRAIN);
-	if (!retval) {
-		stream->runtime->state = SNDRV_PCM_STATE_DRAINING;
+	if (retval) {
+		pr_debug("SND_COMPR_TRIGGER_DRAIN failed %d\n", retval);
 		wake_up(&stream->runtime->sleep);
+		return retval;
 	}
+
+	retval = snd_compress_wait_for_drain(stream);
+	stream->runtime->state = SNDRV_PCM_STATE_SETUP;
 	return retval;
 }
 
@@ -735,10 +778,16 @@ static int snd_compr_partial_drain(struct snd_compr_stream *stream)
 	if (stream->next_track == false)
 		return -EPERM;
 
+	stream->runtime->drain_wake = 0;
 	retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_PARTIAL_DRAIN);
+	if (retval) {
+		pr_debug("Partial drain returned failure\n");
+		wake_up(&stream->runtime->sleep);
+		return retval;
+	}
 
 	stream->next_track = false;
-	return retval;
+	return snd_compress_wait_for_drain(stream);
 }
 
 static long snd_compr_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
-- 
1.7.0.4

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

* Re: [PATCH v3] ALSA: compress: fix drain calls blocking other compress functions
  2013-10-30 15:18           ` Vinod Koul
@ 2013-10-30 16:24             ` Takashi Iwai
  2013-10-30 15:33               ` Vinod Koul
  0 siblings, 1 reply; 19+ messages in thread
From: Takashi Iwai @ 2013-10-30 16:24 UTC (permalink / raw)
  To: Vinod Koul; +Cc: alsa-devel, broonie, lgirdwood

At Wed, 30 Oct 2013 20:48:40 +0530,
Vinod Koul wrote:
> 
> On Wed, Oct 30, 2013 at 05:01:38PM +0100, Takashi Iwai wrote:
>  
> > > > > +static int snd_compress_wait_for_drain(struct snd_compr_stream *stream)
> > > > > +{
> > > > > +	/*
> > > > > +	 * We are called with lock held. So drop the lock while we wait for
> > > > > +	 * drain complete notfication from the driver
> > > > > +	 *
> > > > > +	 * It is expected that driver will notify the drain completion and then
> > > > > +	 * stream will be moved to SETUP state, even if draining resulted in an
> > > > > +	 * error. We can trigger next track after this.
> > > > > +	 */
> > > > > +	stream->runtime->state = SNDRV_PCM_STATE_DRAINING;
> > > > > +	mutex_unlock(&stream->device->lock);
> > > > > +
> > > > > +	wait_event(stream->runtime->wait, stream->runtime->drain_wake);
> > > > 
> > > > Don't you really want the interruption?
> > > > Or use wait_event_interruptible() and return error appropriately.
> > > any interruption from usermode should trigger the compress_stop/compress_free
> > 
> > ... but how?
> > 
> > > which will unblock this. I dont see the need to have interruptible here
> > 
> > Suppose you're running a single thread program.  Then who triggers the
> > stop?
> Your signal catcher which should be registered for signals intrested and then
> calls stop or free. Assuming this should be considered single threaded. IIRC,
> this is how you do in aplay, right?

No, issuing an ioctl in a signal isn't guaranteed to work at all.
You shouldn't code the kernel driver relying on that userspace
behavior.


Takashi

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

* Re: [PATCH v3] ALSA: compress: fix drain calls blocking other compress functions
  2013-10-30 15:33               ` Vinod Koul
@ 2013-10-30 16:38                 ` Takashi Iwai
  0 siblings, 0 replies; 19+ messages in thread
From: Takashi Iwai @ 2013-10-30 16:38 UTC (permalink / raw)
  To: Vinod Koul; +Cc: alsa-devel, broonie, lgirdwood

At Wed, 30 Oct 2013 21:03:54 +0530,
Vinod Koul wrote:
> 
> On Wed, Oct 30, 2013 at 05:24:12PM +0100, Takashi Iwai wrote:
> > At Wed, 30 Oct 2013 20:48:40 +0530,
> > Vinod Koul wrote:
> > > 
> > > On Wed, Oct 30, 2013 at 05:01:38PM +0100, Takashi Iwai wrote:
> > >  
> > > > > > > +static int snd_compress_wait_for_drain(struct snd_compr_stream *stream)
> > > > > > > +{
> > > > > > > +	/*
> > > > > > > +	 * We are called with lock held. So drop the lock while we wait for
> > > > > > > +	 * drain complete notfication from the driver
> > > > > > > +	 *
> > > > > > > +	 * It is expected that driver will notify the drain completion and then
> > > > > > > +	 * stream will be moved to SETUP state, even if draining resulted in an
> > > > > > > +	 * error. We can trigger next track after this.
> > > > > > > +	 */
> > > > > > > +	stream->runtime->state = SNDRV_PCM_STATE_DRAINING;
> > > > > > > +	mutex_unlock(&stream->device->lock);
> > > > > > > +
> > > > > > > +	wait_event(stream->runtime->wait, stream->runtime->drain_wake);
> > > > > > 
> > > > > > Don't you really want the interruption?
> > > > > > Or use wait_event_interruptible() and return error appropriately.
> > > > > any interruption from usermode should trigger the compress_stop/compress_free
> > > > 
> > > > ... but how?
> > > > 
> > > > > which will unblock this. I dont see the need to have interruptible here
> > > > 
> > > > Suppose you're running a single thread program.  Then who triggers the
> > > > stop?
> > > Your signal catcher which should be registered for signals intrested and then
> > > calls stop or free. Assuming this should be considered single threaded. IIRC,
> > > this is how you do in aplay, right?
> > 
> > No, issuing an ioctl in a signal isn't guaranteed to work at all.
> Okay thanks for pointing out, do you know why we have that
> limitation.

It's a long-standing limitation of POSIX.
Basically a signal handler can accept only very few functions that are
asynchronous-safe.  That's the reason why signal is practically
useless in most use cases.

>  Btw
> closing the fd should have same effect as free would trigger the stop internally

Well, better to test it before jumping into the conclusion :)

> > You shouldn't code the kernel driver relying on that userspace
> > behavior.
> Ok, will update it

OK.


thanks,

Takashi

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

* Re: [PATCH v4] ALSA: compress: fix drain calls blocking other compress functions
  2013-10-31  7:40       ` Takashi Iwai
@ 2013-10-31  7:18         ` Vinod Koul
  0 siblings, 0 replies; 19+ messages in thread
From: Vinod Koul @ 2013-10-31  7:18 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: alsa-devel, broonie, lgirdwood

On Thu, Oct 31, 2013 at 08:40:14AM +0100, Takashi Iwai wrote:
> At Wed, 30 Oct 2013 21:43:27 +0530,
> Vinod Koul wrote:
> > 
> > The drain and drain_notify callback were blocked by low level driver untill the
> > draining was complete. Due to this being invoked with big fat mutex held, others
> > ops like reading timestamp, calling pause, drop were blocked.
> > 
> > So to fix this we add a new snd_compr_drain_notify() API. This would be required
> > to be invoked by low level driver when drain or partial drain has been completed
> > by the DSP. Thus we make the drain and partial_drain callback as non blocking
> > and driver returns immediately after notifying DSP.
> > The waiting is done while relasing the lock so that other ops can go ahead.
> > 
> > Signed-off-by: Vinod Koul <vinod.koul@intel.com>
> > CC: stable@vger.kernel.org
> > ---
> > v4:
> >  move pr_err -> pr_debug to avoid spamming kernel log
> >  make wait in drain interruptible
> > v3:
> >  call snd_compr_drain_notify from compress_stop()
> >  rename draining -> drain_wake
> >  add some comments on state transistion after drain
> > v2:
> >  fix the 80 line warn
> >  move the state change to compress_drain()
> > 
> > 
> >  include/sound/compress_driver.h |   13 +++++++++
> >  sound/core/compress_offload.c   |   55 ++++++++++++++++++++++++++++++++++++--
> >  2 files changed, 65 insertions(+), 3 deletions(-)
> > 
> > diff --git a/include/sound/compress_driver.h b/include/sound/compress_driver.h
> > index 9031a26..e723935 100644
> > --- a/include/sound/compress_driver.h
> > +++ b/include/sound/compress_driver.h
> > @@ -48,6 +48,8 @@ struct snd_compr_ops;
> >   *	the ring buffer
> >   * @total_bytes_transferred: cumulative bytes transferred by offload DSP
> >   * @sleep: poll sleep
> > + * @wait: drain wait queue
> > + * @drain_wake: condition for drain wake
> >   */
> >  struct snd_compr_runtime {
> >  	snd_pcm_state_t state;
> > @@ -59,6 +61,8 @@ struct snd_compr_runtime {
> >  	u64 total_bytes_available;
> >  	u64 total_bytes_transferred;
> >  	wait_queue_head_t sleep;
> > +	wait_queue_head_t wait;
> 
> I took a look back at the code, and now wonder why you can't use the
> same wait queue (sleep) for drain?  PCM code uses the same waitqueue.
> 
> > +	unsigned int drain_wake;
> 
> Also, drain_wake can be omitted by checking the runtime state
> instead, e.g.
> 	wait_event_interruptible(runtime->sleep,
> 				 runtime->state != SNDRV_PCM_STATE_DRAINING);
> 
> while snd_compr_drain_notify() would be
> 
> static inline void snd_compr_drain_notify(struct snd_compr_stream *stream)
> {
> 	stream->runtime->state = SNDRV_PCM_STATE_SETUP;
> 	wake_up(&stream->runtime->sleep);
> }
> 
> (And, if we still use drain_wake, it can be better bool instead of
> unsigned int.)
Hmmm, it started out thinking we can reuse the existing sleep but then went
ahead with a new one. I will test the changes and update this

--
~Vinod

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

* Re: [PATCH v4] ALSA: compress: fix drain calls blocking other compress functions
  2013-10-30 16:13     ` [PATCH v4] " Vinod Koul
@ 2013-10-31  7:40       ` Takashi Iwai
  2013-10-31  7:18         ` Vinod Koul
  0 siblings, 1 reply; 19+ messages in thread
From: Takashi Iwai @ 2013-10-31  7:40 UTC (permalink / raw)
  To: Vinod Koul; +Cc: alsa-devel, broonie, lgirdwood

At Wed, 30 Oct 2013 21:43:27 +0530,
Vinod Koul wrote:
> 
> The drain and drain_notify callback were blocked by low level driver untill the
> draining was complete. Due to this being invoked with big fat mutex held, others
> ops like reading timestamp, calling pause, drop were blocked.
> 
> So to fix this we add a new snd_compr_drain_notify() API. This would be required
> to be invoked by low level driver when drain or partial drain has been completed
> by the DSP. Thus we make the drain and partial_drain callback as non blocking
> and driver returns immediately after notifying DSP.
> The waiting is done while relasing the lock so that other ops can go ahead.
> 
> Signed-off-by: Vinod Koul <vinod.koul@intel.com>
> CC: stable@vger.kernel.org
> ---
> v4:
>  move pr_err -> pr_debug to avoid spamming kernel log
>  make wait in drain interruptible
> v3:
>  call snd_compr_drain_notify from compress_stop()
>  rename draining -> drain_wake
>  add some comments on state transistion after drain
> v2:
>  fix the 80 line warn
>  move the state change to compress_drain()
> 
> 
>  include/sound/compress_driver.h |   13 +++++++++
>  sound/core/compress_offload.c   |   55 ++++++++++++++++++++++++++++++++++++--
>  2 files changed, 65 insertions(+), 3 deletions(-)
> 
> diff --git a/include/sound/compress_driver.h b/include/sound/compress_driver.h
> index 9031a26..e723935 100644
> --- a/include/sound/compress_driver.h
> +++ b/include/sound/compress_driver.h
> @@ -48,6 +48,8 @@ struct snd_compr_ops;
>   *	the ring buffer
>   * @total_bytes_transferred: cumulative bytes transferred by offload DSP
>   * @sleep: poll sleep
> + * @wait: drain wait queue
> + * @drain_wake: condition for drain wake
>   */
>  struct snd_compr_runtime {
>  	snd_pcm_state_t state;
> @@ -59,6 +61,8 @@ struct snd_compr_runtime {
>  	u64 total_bytes_available;
>  	u64 total_bytes_transferred;
>  	wait_queue_head_t sleep;
> +	wait_queue_head_t wait;

I took a look back at the code, and now wonder why you can't use the
same wait queue (sleep) for drain?  PCM code uses the same waitqueue.

> +	unsigned int drain_wake;

Also, drain_wake can be omitted by checking the runtime state
instead, e.g.
	wait_event_interruptible(runtime->sleep,
				 runtime->state != SNDRV_PCM_STATE_DRAINING);

while snd_compr_drain_notify() would be

static inline void snd_compr_drain_notify(struct snd_compr_stream *stream)
{
	stream->runtime->state = SNDRV_PCM_STATE_SETUP;
	wake_up(&stream->runtime->sleep);
}

(And, if we still use drain_wake, it can be better bool instead of
unsigned int.)


thanks,

Takashi

>  	void *private_data;
>  };
>  
> @@ -171,4 +175,13 @@ static inline void snd_compr_fragment_elapsed(struct snd_compr_stream *stream)
>  	wake_up(&stream->runtime->sleep);
>  }
>  
> +static inline void snd_compr_drain_notify(struct snd_compr_stream *stream)
> +{
> +	if (snd_BUG_ON(!stream))
> +		return;
> +
> +	stream->runtime->drain_wake = 1;
> +	wake_up(&stream->runtime->wait);
> +}
> +
>  #endif
> diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c
> index bea523a..645cec5 100644
> --- a/sound/core/compress_offload.c
> +++ b/sound/core/compress_offload.c
> @@ -123,6 +123,7 @@ static int snd_compr_open(struct inode *inode, struct file *f)
>  	}
>  	runtime->state = SNDRV_PCM_STATE_OPEN;
>  	init_waitqueue_head(&runtime->sleep);
> +	init_waitqueue_head(&runtime->wait);
>  	data->stream.runtime = runtime;
>  	f->private_data = (void *)data;
>  	mutex_lock(&compr->lock);
> @@ -682,12 +683,48 @@ static int snd_compr_stop(struct snd_compr_stream *stream)
>  	if (!retval) {
>  		stream->runtime->state = SNDRV_PCM_STATE_SETUP;
>  		wake_up(&stream->runtime->sleep);
> +		snd_compr_drain_notify(stream);
>  		stream->runtime->total_bytes_available = 0;
>  		stream->runtime->total_bytes_transferred = 0;
>  	}
>  	return retval;
>  }
>  
> +static int snd_compress_wait_for_drain(struct snd_compr_stream *stream)
> +{
> +	int ret;
> +
> +	/*
> +	 * We are called with lock held. So drop the lock while we wait for
> +	 * drain complete notfication from the driver
> +	 *
> +	 * It is expected that driver will notify the drain completion and then
> +	 * stream will be moved to SETUP state, even if draining resulted in an
> +	 * error. We can trigger next track after this.
> +	 */
> +	stream->runtime->state = SNDRV_PCM_STATE_DRAINING;
> +	mutex_unlock(&stream->device->lock);
> +
> +	/* we wait for drain to complete here, drain can return when
> +	 * interruption occurred, wait returned error or success.
> +	 * For the first two cases we don't do anything different here and
> +	 * return after waking up
> +	 */
> +
> +	ret = wait_event_interruptible(stream->runtime->wait,
> +			stream->runtime->drain_wake);
> +	if (ret == -ERESTARTSYS)
> +		pr_debug("wait aborted by a signal");
> +	else if (ret)
> +		pr_debug("wait for drain failed with %d\n", ret);
> +
> +
> +	wake_up(&stream->runtime->sleep);
> +	mutex_lock(&stream->device->lock);
> +
> +	return ret;
> +}
> +
>  static int snd_compr_drain(struct snd_compr_stream *stream)
>  {
>  	int retval;
> @@ -695,11 +732,17 @@ static int snd_compr_drain(struct snd_compr_stream *stream)
>  	if (stream->runtime->state == SNDRV_PCM_STATE_PREPARED ||
>  			stream->runtime->state == SNDRV_PCM_STATE_SETUP)
>  		return -EPERM;
> +
> +	stream->runtime->drain_wake = 0;
>  	retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_DRAIN);
> -	if (!retval) {
> -		stream->runtime->state = SNDRV_PCM_STATE_DRAINING;
> +	if (retval) {
> +		pr_debug("SND_COMPR_TRIGGER_DRAIN failed %d\n", retval);
>  		wake_up(&stream->runtime->sleep);
> +		return retval;
>  	}
> +
> +	retval = snd_compress_wait_for_drain(stream);
> +	stream->runtime->state = SNDRV_PCM_STATE_SETUP;
>  	return retval;
>  }
>  
> @@ -735,10 +778,16 @@ static int snd_compr_partial_drain(struct snd_compr_stream *stream)
>  	if (stream->next_track == false)
>  		return -EPERM;
>  
> +	stream->runtime->drain_wake = 0;
>  	retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_PARTIAL_DRAIN);
> +	if (retval) {
> +		pr_debug("Partial drain returned failure\n");
> +		wake_up(&stream->runtime->sleep);
> +		return retval;
> +	}
>  
>  	stream->next_track = false;
> -	return retval;
> +	return snd_compress_wait_for_drain(stream);
>  }
>  
>  static long snd_compr_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
> -- 
> 1.7.0.4
> 
> _______________________________________________
> Alsa-devel mailing list
> Alsa-devel@alsa-project.org
> http://mailman.alsa-project.org/mailman/listinfo/alsa-devel
> 

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

end of thread, other threads:[~2013-10-31  8:12 UTC | newest]

Thread overview: 19+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-10-23 16:17 [PATCH] ALSA: compress: fix drain calls blocking other compress functions Vinod Koul
2013-10-24  7:35 ` Vinod Koul
2013-10-24  9:25   ` Takashi Iwai
2013-10-24  9:27     ` Vinod Koul
2013-10-24 10:59       ` Takashi Iwai
2013-10-24 10:35         ` Vinod Koul
2013-10-24 12:00           ` Takashi Iwai
2013-10-24 11:08             ` Vinod Koul
2013-10-24 11:07   ` [PATCH v3] " Vinod Koul
2013-10-30 14:47     ` Takashi Iwai
2013-10-30 14:58       ` Vinod Koul
2013-10-30 16:01         ` Takashi Iwai
2013-10-30 15:18           ` Vinod Koul
2013-10-30 16:24             ` Takashi Iwai
2013-10-30 15:33               ` Vinod Koul
2013-10-30 16:38                 ` Takashi Iwai
2013-10-30 16:13     ` [PATCH v4] " Vinod Koul
2013-10-31  7:40       ` Takashi Iwai
2013-10-31  7:18         ` Vinod Koul

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.