[alsa-devel] usb audio race at disconnect time

Matthieu CASTET matthieu.castet at parrot.com
Fri Oct 12 17:42:19 CEST 2012


Hi,


Takashi Iwai a écrit :
> [Added Daniel and Clemens in the loop]
> 
> 
> I don't think this is needed.
> 
> So... the below is a quick hack I did without testing at all.
> Hopefully this can give some advance.
Thanks for the quick patch.

The patch didn't apply cleany of linus tree, of which tree is based your patch ?

It solve the race in snd_usb_hw_params.

But I wonder if snd_usb_substream_playback_trigger,
snd_usb_substream_capture_trigger, snd_usb_pcm_pointer are safe : they don't
take shutdown_mutex.

Also it is hard to check if everything is safe. Sometimes the chip->shutdown is
far from the shutdown_mutex. For example snd_usb_pcm_open take shutdown_mutex,
but the check is done in snd_usb_autoresume.

I will do more testing.

Thanks,

Matthieu
> 
> 
> Takashi
> 
> ---
> diff --git a/sound/usb/card.c b/sound/usb/card.c
> index 561bb74..115484e 100644
> --- a/sound/usb/card.c
> +++ b/sound/usb/card.c
> @@ -131,9 +131,13 @@ static void snd_usb_stream_disconnect(struct list_head *head)
>  		subs = &as->substream[idx];
>  		if (!subs->num_formats)
>  			continue;
> +		if (subs->pcm_substream)
> +			snd_pcm_stream_lock_irq(subs->pcm_substream);
>  		subs->interface = -1;
>  		subs->data_endpoint = NULL;
>  		subs->sync_endpoint = NULL;
> +		if (subs->pcm_substream)
> +			snd_pcm_stream_unlock_irq(subs->pcm_substream);
>  	}
>  }
>  
> diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c
> index 55e19e1..01e82ac 100644
> --- a/sound/usb/pcm.c
> +++ b/sound/usb/pcm.c
> @@ -444,7 +444,6 @@ static int configure_endpoint(struct snd_usb_substream *subs)
>  {
>  	int ret;
>  
> -	mutex_lock(&subs->stream->chip->shutdown_mutex);
>  	/* format changed */
>  	stop_endpoints(subs, 0, 0, 0);
>  	ret = snd_usb_endpoint_set_params(subs->data_endpoint,
> @@ -455,7 +454,7 @@ static int configure_endpoint(struct snd_usb_substream *subs)
>  					  subs->cur_audiofmt,
>  					  subs->sync_endpoint);
>  	if (ret < 0)
> -		goto unlock;
> +		return ret;
>  
>  	if (subs->sync_endpoint)
>  		ret = snd_usb_endpoint_set_params(subs->data_endpoint,
> @@ -466,8 +465,6 @@ static int configure_endpoint(struct snd_usb_substream *subs)
>  						  subs->cur_audiofmt,
>  						  NULL);
>  
> -unlock:
> -	mutex_unlock(&subs->stream->chip->shutdown_mutex);
>  	return ret;
>  }
>  
> @@ -488,10 +485,15 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream,
>  	struct audioformat *fmt;
>  	int ret;
>  
> +	mutex_lock(&subs->stream->chip->shutdown_mutex);
> +	if (subs->stream->chip->shutdown) {
> +		ret = -ENODEV;
> +		goto unlock;
> +	}
>  	ret = snd_pcm_lib_alloc_vmalloc_buffer(substream,
>  					       params_buffer_bytes(hw_params));
>  	if (ret < 0)
> -		return ret;
> +		goto unlock;
>  
>  	subs->pcm_format = params_format(hw_params);
>  	subs->period_bytes = params_period_bytes(hw_params);
> @@ -502,17 +504,20 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream,
>  	if (!fmt) {
>  		snd_printd(KERN_DEBUG "cannot set format: format = %#x, rate = %d, channels = %d\n",
>  			   subs->pcm_format, subs->cur_rate, subs->channels);
> -		return -EINVAL;
> +		ret = -EINVAL;
> +		goto unlock;
>  	}
>  
>  	if ((ret = set_format(subs, fmt)) < 0)
> -		return ret;
> +		goto unlock;
>  
>  	subs->interface = fmt->iface;
>  	subs->altset_idx = fmt->altset_idx;
>  	subs->need_setup_ep = true;
>  
> -	return 0;
> + unlock:
> +	mutex_unlock(&subs->stream->chip->shutdown_mutex);
> +	return ret;
>  }
>  
>  /*
> @@ -547,17 +552,26 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream)
>  	struct usb_interface *iface;
>  	int ret;
>  
> +	mutex_lock(&subs->stream->chip->shutdown_mutex);
> +	if (subs->stream->chip->shutdown) {
> +		ret = -ENODEV;
> +		goto unlock;
> +	}
> +
>  	if (! subs->cur_audiofmt) {
>  		snd_printk(KERN_ERR "usbaudio: no format is specified!\n");
> -		return -ENXIO;
> +		ret = -ENXIO;
> +		goto unlock;
>  	}
>  
> -	if (snd_BUG_ON(!subs->data_endpoint))
> -		return -EIO;
> +	if (snd_BUG_ON(!subs->data_endpoint)) {
> +		ret = -EIO;
> +		goto unlock;
> +	}
>  
>  	ret = set_format(subs, subs->cur_audiofmt);
>  	if (ret < 0)
> -		return ret;
> +		goto unlock;
>  
>  	iface = usb_ifnum_to_if(subs->dev, subs->cur_audiofmt->iface);
>  	alts = &iface->altsetting[subs->cur_audiofmt->altset_idx];
> @@ -567,12 +581,12 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream)
>  				       subs->cur_audiofmt,
>  				       subs->cur_rate);
>  	if (ret < 0)
> -		return ret;
> +		goto unlock;
>  
>  	if (subs->need_setup_ep) {
>  		ret = configure_endpoint(subs);
>  		if (ret < 0)
> -			return ret;
> +			goto unlock;
>  		subs->need_setup_ep = false;
>  	}
>  
> @@ -592,8 +606,10 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream)
>  	/* for playback, submit the URBs now; otherwise, the first hwptr_done
>  	 * updates for all URBs would happen at the same time when starting */
>  	if (subs->direction == SNDRV_PCM_STREAM_PLAYBACK)
> -		return start_endpoints(subs, 1);
> +		ret = start_endpoints(subs, 1);
>  
> + unlock:
> +	mutex_unlock(&subs->stream->chip->shutdown_mutex);
>  	return 0;
>  }
>  
> @@ -980,14 +996,18 @@ static int snd_usb_pcm_open(struct snd_pcm_substream *substream, int direction)
>  	struct snd_usb_stream *as = snd_pcm_substream_chip(substream);
>  	struct snd_pcm_runtime *runtime = substream->runtime;
>  	struct snd_usb_substream *subs = &as->substream[direction];
> +	int ret;
>  
> +	mutex_lock(&as->chip->shutdown_mutex);
>  	subs->interface = -1;
>  	subs->altset_idx = 0;
>  	runtime->hw = snd_usb_hardware;
>  	runtime->private_data = subs;
>  	subs->pcm_substream = substream;
>  	/* runtime PM is also done there */
> -	return setup_hw_info(runtime, subs);
> +	ret = setup_hw_info(runtime, subs);
> +	mutex_unlock(&as->chip->shutdown_mutex);
> +	return ret;
>  }
>  
>  static int snd_usb_pcm_close(struct snd_pcm_substream *substream, int direction)
> @@ -995,6 +1015,7 @@ static int snd_usb_pcm_close(struct snd_pcm_substream *substream, int direction)
>  	struct snd_usb_stream *as = snd_pcm_substream_chip(substream);
>  	struct snd_usb_substream *subs = &as->substream[direction];
>  
> +	mutex_lock(&as->chip->shutdown_mutex);
>  	stop_endpoints(subs, 0, 0, 0);
>  
>  	if (!as->chip->shutdown && subs->interface >= 0) {
> @@ -1004,6 +1025,7 @@ static int snd_usb_pcm_close(struct snd_pcm_substream *substream, int direction)
>  
>  	subs->pcm_substream = NULL;
>  	snd_usb_autosuspend(subs->stream->chip);
> +	mutex_unlock(&as->chip->shutdown_mutex);
>  
>  	return 0;
>  }
> @@ -1222,6 +1244,9 @@ static int snd_usb_substream_playback_trigger(struct snd_pcm_substream *substrea
>  {
>  	struct snd_usb_substream *subs = substream->runtime->private_data;
>  
> +	if (subs->stream->chip->shutdown)
> +		return -ENODEV;
> +
>  	switch (cmd) {
>  	case SNDRV_PCM_TRIGGER_START:
>  	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
> 


More information about the Alsa-devel mailing list