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

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.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
Takashi Iwai 2016-02-10 11:53:30 +01:00
parent 4dff5c7b70
commit 9984d1b583

View File

@ -318,25 +318,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) {
@ -345,11 +334,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);
@ -363,18 +348,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;
}