ALSA: timer: Protect the whole snd_timer_close() with open race

commit 9984d1b5835ca29fc7025186a891ee7398d21cc7 upstream.

In order to make the open/close more robust, widen the register_mutex
protection over the whole snd_timer_close() function.  Also, the close
procedure is slightly shuffled to be in the safer order, as well as a
few code refactoring.

Change-Id: I91f84b4eacddaf3347ec79ee81190021e3612dae
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
This commit is contained in:
Takashi Iwai 2016-02-10 11:53:30 +01:00 committed by voidanix
parent 85a9ae72d3
commit dce5a436fc
1 changed files with 21 additions and 27 deletions

View File

@ -326,25 +326,14 @@ int snd_timer_close(struct snd_timer_instance *timeri)
if (snd_BUG_ON(!timeri))
return -ENXIO;
mutex_lock(&register_mutex);
list_del(&timeri->open_list);
/* force to stop the timer */
snd_timer_stop(timeri);
if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) {
/* wait, until the active callback is finished */
spin_lock_irq(&slave_active_lock);
while (timeri->flags & SNDRV_TIMER_IFLG_CALLBACK) {
spin_unlock_irq(&slave_active_lock);
udelay(10);
spin_lock_irq(&slave_active_lock);
}
spin_unlock_irq(&slave_active_lock);
mutex_lock(&register_mutex);
list_del(&timeri->open_list);
mutex_unlock(&register_mutex);
} else {
timer = timeri->timer;
if (snd_BUG_ON(!timer))
goto out;
timer = timeri->timer;
if (timer) {
/* wait, until the active callback is finished */
spin_lock_irq(&timer->lock);
while (timeri->flags & SNDRV_TIMER_IFLG_CALLBACK) {
@ -353,11 +342,7 @@ int snd_timer_close(struct snd_timer_instance *timeri)
spin_lock_irq(&timer->lock);
}
spin_unlock_irq(&timer->lock);
mutex_lock(&register_mutex);
list_del(&timeri->open_list);
if (list_empty(&timer->open_list_head) &&
timer->hw.close)
timer->hw.close(timer);
/* remove slave links */
spin_lock_irq(&slave_active_lock);
spin_lock(&timer->lock);
@ -371,18 +356,27 @@ int snd_timer_close(struct snd_timer_instance *timeri)
}
spin_unlock(&timer->lock);
spin_unlock_irq(&slave_active_lock);
/* release a card refcount for safe disconnection */
if (timer->card)
put_device(timer->card->card_dev);
mutex_unlock(&register_mutex);
/* slave doesn't need to release timer resources below */
if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE)
timer = NULL;
}
out:
if (timeri->private_free)
timeri->private_free(timeri);
kfree(timeri->owner);
kfree(timeri);
if (timer)
if (timer) {
if (list_empty(&timer->open_list_head) && timer->hw.close)
timer->hw.close(timer);
/* release a card refcount for safe disconnection */
if (timer->card)
put_device(timer->card->card_dev);
module_put(timer->module);
}
mutex_unlock(&register_mutex);
return 0;
}